UNPKG

@mark01/express-utils

Version:

npm package that contains utilities for express.js

89 lines (88 loc) 3.23 kB
import * as OTPAuth from 'otpauth'; import timingSafeStringCompare from '../util/timing-safe-string-compare'; import { b32FromBuf, b32ToBuf } from './util/base32'; import hmacDigest from './util/hmac-digest'; import numberToBuf from './util/number-to-buf'; import pad from './util/pad'; export const generateDefaultTOTP = (issuer, label, secret) => { const otpSecret = OTPAuth.Secret.fromBase32(b32FromBuf(Buffer.from(secret))); const totp = new OTPAuth.TOTP({ issuer, label, algorithm: 'SHA1', digits: 6, period: 30, secret: otpSecret, }); return { token: totp.generate(), uri: totp.toString() }; }; export const generateTOTP = ({ issuer, label, algorithm, digits, period, secret, }) => { const otpSecret = OTPAuth.Secret.fromBase32(b32FromBuf(Buffer.from(secret))); const totp = new OTPAuth.TOTP({ issuer, label, algorithm, digits, period, secret: otpSecret, }); return { token: totp.generate(), uri: totp.toString() }; }; export const validateDefaultTOTP = (issuer, token, secret) => { const otpSecret = OTPAuth.Secret.fromBase32(b32FromBuf(Buffer.from(secret))); const totp = new OTPAuth.TOTP({ issuer, algorithm: 'SHA1', digits: 6, period: 30, secret: otpSecret, }); const delta = totp.validate({ token, window: 2 }); return delta !== null && delta >= -2; }; export const validateTOTP = (token, { issuer, label, algorithm, digits, period, secret }) => { const otpSecret = OTPAuth.Secret.fromBase32(b32FromBuf(Buffer.from(secret))); const totp = new OTPAuth.TOTP({ issuer, label, algorithm, digits, period, secret: otpSecret, }); const delta = totp.validate({ token, window: 2 }); return delta !== null && delta >= -2; }; export const generateOwnOTP = (counter, { issuer, label, algorithm, digits, period, secret }) => { const b32Secret = b32ToBuf(b32FromBuf(Buffer.from(secret))); const epoch = numberToBuf(counter); const digest = new Uint8Array(hmacDigest(algorithm, b32Secret, epoch)); const offset = digest[digest.byteLength - 1] & 15; const otp = (((digest[offset] & 127) << 24) | ((digest[offset + 1] & 255) << 16) | ((digest[offset + 2] & 255) << 8) | (digest[offset + 3] & 255)) % 10 ** digits; const token = pad(otp, digits); const uri = `otpauth://totp/${issuer}:${label}?issuer=${issuer}&secret=${secret}&algorithm=${algorithm}&digits=${digits}&period=${period}`; return { token, uri: encodeURI(uri) }; }; export const verifyOwnTOTP = (otp, window, { issuer, label, algorithm, digits, period, secret }) => { if (otp.length !== digits) return false; const counter = Math.floor(Date.now() / 1000 / period); for (let i = counter - window; i <= counter + window; i++) { const generatedOTP = generateOwnOTP(i, { issuer, label, algorithm, digits, period, secret, }).token; if (timingSafeStringCompare(otp, generatedOTP)) { return true; } } return false; };