@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
JavaScript
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, '')));