@mark01/express-utils
Version:
npm package that contains utilities for express.js
89 lines (88 loc) • 3.23 kB
JavaScript
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;
};