From e50b643d74a54516768abf4a6814671e7e1fe4b8 Mon Sep 17 00:00:00 2001 From: Sergio Moreno Date: Tue, 4 Apr 2023 11:55:17 +0200 Subject: [PATCH] feat(): upgrade speakeasy to maintained version --- package-lock.json | 43 ++++-------- package.json | 2 +- totp.js | 173 +++++++++++++++++++++++----------------------- 3 files changed, 101 insertions(+), 117 deletions(-) diff --git a/package-lock.json b/package-lock.json index 436827b..0ddfeeb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,9 @@ "version": "1.0.0", "license": "MIT", "dependencies": { + "@levminer/speakeasy": "^1.4.2", "fastify-plugin": "^3.0.0", - "qrcode": "^1.4.4", - "speakeasy": "^2.0.0" + "qrcode": "^1.4.4" }, "devDependencies": { "eslint": "^7.27.0", @@ -485,6 +485,11 @@ "node": ">=8" } }, + "node_modules/@levminer/speakeasy": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@levminer/speakeasy/-/speakeasy-1.4.2.tgz", + "integrity": "sha512-Iykx4w3dedNz4xmv6MBVQfqe9fQLcH5Y6nX1zKOGkYh6fzGB5h0HR3VfQ+5xiSmN1GGCXIdkLDWNiHUv/no5wQ==" + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -683,11 +688,6 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "node_modules/base32.js": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.0.1.tgz", - "integrity": "sha1-0EVzalex9sE58MffQlGKhOkbsro=" - }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -4189,17 +4189,6 @@ "integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ==", "dev": true }, - "node_modules/speakeasy": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/speakeasy/-/speakeasy-2.0.0.tgz", - "integrity": "sha1-hckaBxsJpcuGQlkNmDVmFl9XYTo=", - "dependencies": { - "base32.js": "0.0.1" - }, - "engines": { - "node": ">= 0.10.0" - } - }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -7141,6 +7130,11 @@ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true }, + "@levminer/speakeasy": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@levminer/speakeasy/-/speakeasy-1.4.2.tgz", + "integrity": "sha512-Iykx4w3dedNz4xmv6MBVQfqe9fQLcH5Y6nX1zKOGkYh6fzGB5h0HR3VfQ+5xiSmN1GGCXIdkLDWNiHUv/no5wQ==" + }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -7300,11 +7294,6 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "base32.js": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.0.1.tgz", - "integrity": "sha1-0EVzalex9sE58MffQlGKhOkbsro=" - }, "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -10124,14 +10113,6 @@ "integrity": "sha512-Ki212dKK4ogX+xDo4CtOZBVIwhsKBEfsEEcwmJfLQzirgc2jIWdzg40Unxz/HzEUqM1WFzVlQSMF9kZZ2HboLQ==", "dev": true }, - "speakeasy": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/speakeasy/-/speakeasy-2.0.0.tgz", - "integrity": "sha1-hckaBxsJpcuGQlkNmDVmFl9XYTo=", - "requires": { - "base32.js": "0.0.1" - } - }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", diff --git a/package.json b/package.json index f822dfb..47d77ba 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "dependencies": { "fastify-plugin": "^3.0.0", "qrcode": "^1.4.4", - "speakeasy": "^2.0.0" + "@levminer/speakeasy": "^1.4.2" }, "tap": { "check-coverage": false diff --git a/totp.js b/totp.js index 3311266..57deae2 100644 --- a/totp.js +++ b/totp.js @@ -1,86 +1,89 @@ -'use strict' - -const fp = require('fastify-plugin') -const speakeasy = require('speakeasy') -const qrcode = require('qrcode') - -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 - -module.exports = fp(function (fastify, opts, next) { - const TOTP_SECRET_LENGHT = 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.generateSecret({ - length: length || TOTP_SECRET_LENGHT - }) - // WARNING: secret is NOT a string, but an object providing - // the secret key encoded in several ways (ascii, base32, etc.) - return secret +'use strict'; + +const fp = require('fastify-plugin'); +const speakeasy = require('@levminer/speakeasy'); +const qrcode = require('qrcode'); + +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; + +module.exports = fp( + function (fastify, opts, next) { + const TOTP_SECRET_LENGHT = 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.generateSecret({ + length: length || TOTP_SECRET_LENGHT, + }); + // WARNING: secret is NOT a string, but an object providing + // the secret key encoded in several ways (ascii, base32, etc.) + return secret; + } + + function generateTOTPToken(options = {}) { + if (!options.secret) return null; + + const token = speakeasy.totp({ + encoding: options.encoding || 'ascii', + algorithm: options.algorithm || TOTP_ALG, + step: options.step || TOTP_STEP, + ...options, + }); + + return token; + } + + function generateAuthURLFromSecret(options = {}) { + if (!options.secret) return null; + + const url = speakeasy.otpauthURL({ + label: options.label || TOTP_LABEL, + algorithm: options.algorithm || TOTP_ALG, + ...options, + }); + + return url; + } + + async function generateQRCodeFromSecret(secret, label, algorithm) { + const url = fastify.totp.generateAuthURL(secret, label, algorithm); + + if (!url) return null; + + return qrcode.toDataURL(url); + } + + function verifyTOTP(options = {}) { + const result = speakeasy.totp.verifyDelta({ + 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, + }); + + fastify.decorateRequest('totpVerify', verifyTOTP); + + next(); + }, + { + fastify: '>=2.x', + name: 'fastify-totp', } - - function generateTOTPToken (options = {}) { - if (!options.secret) return null - - const token = speakeasy.totp({ - encoding: options.encoding || 'ascii', - algorithm: options.algorithm || TOTP_ALG, - step: options.step || TOTP_STEP, - ...options - }) - - return token - } - - function generateAuthURLFromSecret (options = {}) { - if (!options.secret) return null - - const url = speakeasy.otpauthURL({ - label: options.label || TOTP_LABEL, - algorithm: options.algorithm || TOTP_ALG, - ...options - }) - - return url - } - - async function generateQRCodeFromSecret (secret, label, algorithm) { - const url = fastify.totp.generateAuthURL(secret, label, algorithm) - - if (!url) return null - - return qrcode.toDataURL(url) - } - - function verifyTOTP (options = {}) { - const result = speakeasy.totp.verifyDelta({ - 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 - }) - - fastify.decorateRequest('totpVerify', verifyTOTP) - - next() -}, { - fastify: '>=2.x', - name: 'fastify-totp' -}) +);