UNPKG

@imajin/rx-otp

Version:

HMAC-based (HOTP) and Time-based (TOTP) One-Time Password manager. Works with Google Authenticator for Two-Factor Authentication.

67 lines (66 loc) 3.25 kB
import { Buffer } from 'buffer'; import * as crypto from 'crypto'; import * as qr from 'qr-image'; import { mergeMap, of } from 'rxjs'; import { map } from 'rxjs/operators'; import { decode, encode } from 'thirty-two'; import { TOTP } from './totp'; import { Validator } from '../schemas/validator'; export class U2F { } U2F.generateOTPKey = (asBuffer = false) => of(crypto.randomBytes(20).toString('hex')) .pipe(map((key) => !!asBuffer ? Buffer.from(key, 'hex') : key)); U2F.encodeAuthKey = (buffer) => of(buffer) .pipe(map(_ => encode(_).toString().replace(/=/g, '')), map(_ => _.replace(/(\w{4})/g, '$1 ').trim())); U2F.decodeAuthKey = (base32_key) => U2F._cleanBase32Key(base32_key) .pipe(map(_ => decode(_))); U2F.generateAuthKey = () => U2F.generateOTPKey(true) .pipe(mergeMap((_) => U2F.encodeAuthKey(_))); U2F.generateTOTPUri = (secret, account_name, issuer, options = {}) => U2F._cleanBase32Key(secret) .pipe(map(_ => Object.assign({}, options, { secret: _, account_name: account_name, issuer: issuer })), mergeMap((data) => Validator.validateDataWithSchemaReference('/rx-otp/schemas/u2f-uri.json', data)), map((_) => `otpauth://totp/${encodeURI(_.issuer)}:${encodeURI(_.account_name)}?secret=${_.secret}&issuer=${encodeURIComponent(_.issuer)}&algorithm=${_.algorithm}&digits=${_.code_digits}&period=${_.time}`)); U2F.generateAuthToken = (base32_key, options = {}) => U2F.decodeAuthKey(base32_key) .pipe(map((_) => Object.assign({}, options, { key: _.toString('hex') })), mergeMap((data) => Validator.validateDataWithSchemaReference('/rx-otp/schemas/u2f-generate.json', data)), map((_) => ({ key: _.key, options: { key_format: 'hex', time: _.time, timestamp: _.timestamp, code_digits: _.code_digits, add_checksum: _.add_checksum, truncation_offset: _.truncation_offset, algorithm: _.algorithm } })), mergeMap((_) => TOTP.generate(_.key, _.options))); U2F.verifyAuthToken = (token, base32_key, options = {}) => U2F.decodeAuthKey(base32_key) .pipe(map((_) => Object.assign({}, options, { token: token, key: _.toString('hex') })), mergeMap((data) => Validator.validateDataWithSchemaReference('/rx-otp/schemas/u2f-verify.json', data)), map((_) => ({ token: _.token, key: _.key, options: { key_format: 'hex', window: _.window, time: _.time, timestamp: _.timestamp, add_checksum: _.add_checksum, truncation_offset: _.truncation_offset, algorithm: _.algorithm } })), mergeMap((_) => TOTP.verify(_.token, _.key, _.options))); U2F.qrCode = (text, options = {}) => of(Object.assign({}, options, { text: text })) .pipe(mergeMap((data) => Validator.validateDataWithSchemaReference('/rx-otp/schemas/u2f-qr.json', data)), map((_) => ({ text: _.text, options: { ec_level: _.ec_level, type: _.type, size: _.size } })), map(_ => !!_.options.size ? _ : ({ text: _.text, options: { ec_level: _.options.ec_level, type: _.options.type, } })), map((_) => qr.imageSync(_.text, _.options))); U2F._cleanBase32Key = (base32_key) => of(base32_key) .pipe(map(_ => _.replace(/[\s\.\_\-]+/g, '')));