UNPKG

@sergiomorenoalbert/fastify-totp

Version:

A plugin to handle TOTP (e.g. for 2FA)

134 lines (128 loc) 4.65 kB
Object.defineProperty(exports, '__esModule', { value: true }); var fp = require('fastify-plugin'); var speakeasy = require('@levminer/speakeasy'); var qrcode = require('qrcode'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var fp__default = /*#__PURE__*/_interopDefault(fp); var speakeasy__default = /*#__PURE__*/_interopDefault(speakeasy); var qrcode__default = /*#__PURE__*/_interopDefault(qrcode); function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _async_to_generator(fn) { return function() { var self = this, args = arguments; return new Promise(function(resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } function _extends() { _extends = Object.assign || function(target) { for(var i = 1; i < arguments.length; i++){ var source = arguments[i]; for(var key in source){ if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } const DEFAULT_TOTP_SECRET_LENGTH = 20; const DEFAULT_TOTP_LABEL = 'Fastify'; const DEFAULT_TOTP_WINDOW = 1; const DEFAULT_TOTP_ALG = 'sha512'; const DEFAULT_TOTP_STEP = 30; const plugin = function(fastify, opts, next) { const TOTP_SECRET_LENGTH = opts.secretLength || DEFAULT_TOTP_SECRET_LENGTH; const TOTP_LABEL = opts.totpLabel || DEFAULT_TOTP_LABEL; const TOTP_WINDOW = opts.totpWindow || DEFAULT_TOTP_WINDOW; const TOTP_ALG = opts.totpAlg || DEFAULT_TOTP_ALG; const TOTP_STEP = opts.totpStep || DEFAULT_TOTP_STEP; function generateTOTPSecret(length) { const secret = speakeasy__default.default.generateSecret({ length: length || TOTP_SECRET_LENGTH }); return secret; } function generateTOTPToken(options) { if (!options) return null; if (!options.secret) return null; const token = speakeasy__default.default.totp(_extends({ encoding: options.encoding || 'ascii', algorithm: options.algorithm || TOTP_ALG, step: options.step || TOTP_STEP }, options)); return token; } function generateAuthURLFromSecret(options) { if (!options) return null; if (!options.secret) return null; const url = speakeasy__default.default.otpauthURL(_extends({}, options, { algorithm: options.algorithm || TOTP_ALG, label: options.label || TOTP_LABEL })); return url; } function generateQRCodeFromSecret(options) { return _generateQRCodeFromSecret.apply(this, arguments); } function _generateQRCodeFromSecret() { _generateQRCodeFromSecret = _async_to_generator(function*(options) { const url = generateAuthURLFromSecret(options); if (!url) return null; return qrcode__default.default.toDataURL(url); }); return _generateQRCodeFromSecret.apply(this, arguments); } function verifyTOTP(options) { const result = speakeasy__default.default.totp.verifyDelta(_extends({ encoding: options.encoding || 'ascii', window: options.window || TOTP_WINDOW, step: options.step || TOTP_STEP }, options)); return !!result; } fastify.decorate('totp', { generateSecret: generateTOTPSecret, generateToken: generateTOTPToken, generateAuthURL: generateAuthURLFromSecret, generateQRCode: generateQRCodeFromSecret, verify: verifyTOTP, options: { secretLength: TOTP_SECRET_LENGTH, totpLabel: TOTP_LABEL, totpWindow: TOTP_WINDOW, totpAlg: TOTP_ALG, totpStep: TOTP_STEP } }); fastify.decorateRequest('totpVerify', verifyTOTP); next(); }; var index = fp__default.default(plugin, { fastify: '>=5.x', name: 'fastify-totp' }); exports.default = index;