@sergiomorenoalbert/fastify-totp
Version:
A plugin to handle TOTP (e.g. for 2FA)
134 lines (128 loc) • 4.65 kB
JavaScript
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;