From bb4b5fb3aa2a2022c3b6307de0a726a234154e6b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Feb 2026 21:22:20 +0000 Subject: [PATCH 1/2] Bump otplib from 12.0.1 to 13.2.1 in /backend Bumps [otplib](https://github.com/yeojz/otplib/tree/HEAD/packages/otplib) from 12.0.1 to 13.2.1. - [Release notes](https://github.com/yeojz/otplib/releases) - [Commits](https://github.com/yeojz/otplib/commits/v13.2.1/packages/otplib) --- updated-dependencies: - dependency-name: otplib dependency-version: 13.2.1 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- backend/package.json | 2 +- backend/yarn.lock | 97 +++++++++++++++++++++++++------------------- 2 files changed, 57 insertions(+), 42 deletions(-) diff --git a/backend/package.json b/backend/package.json index fd77f79a..93ddfdd6 100644 --- a/backend/package.json +++ b/backend/package.json @@ -30,7 +30,7 @@ "mysql2": "^3.16.2", "node-rsa": "^1.1.1", "objection": "3.1.5", - "otplib": "^12.0.1", + "otplib": "^13.2.1", "path": "^0.12.7", "pg": "^8.18.0", "proxy-agent": "^6.5.0", diff --git a/backend/yarn.lock b/backend/yarn.lock index 4a523961..a8688084 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -111,6 +111,11 @@ wrap-ansi "^8.1.0" wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" +"@noble/hashes@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-2.0.1.tgz#fc1a928061d1232b0a52bb754393c37a5216c89e" + integrity sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw== + "@npmcli/fs@^1.0.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.1.1.tgz#72f719fe935e687c56a4faecf3c03d06ba593257" @@ -127,49 +132,61 @@ mkdirp "^1.0.4" rimraf "^3.0.2" -"@otplib/core@^12.0.1": - version "12.0.1" - resolved "https://registry.yarnpkg.com/@otplib/core/-/core-12.0.1.tgz#73720a8cedce211fe5b3f683cd5a9c098eaf0f8d" - integrity sha512-4sGntwbA/AC+SbPhbsziRiD+jNDdIzsZ3JUyfZwjtKyc/wufl1pnSIaG4Uqx8ymPagujub0o92kgBnB89cuAMA== +"@otplib/core@13.2.1": + version "13.2.1" + resolved "https://registry.yarnpkg.com/@otplib/core/-/core-13.2.1.tgz#83449322a390b6b4c045b2e799cbd1f7718f1f64" + integrity sha512-IyfHvYNCyipDxhmJdcUUvUeT3Hz84/GgM6G2G6BTEmnAKPzNA7U0kYGkxKZWY9h23W94RJk4qiClJRJN5zKGvg== -"@otplib/plugin-crypto@^12.0.1": - version "12.0.1" - resolved "https://registry.yarnpkg.com/@otplib/plugin-crypto/-/plugin-crypto-12.0.1.tgz#2b42c624227f4f9303c1c041fca399eddcbae25e" - integrity sha512-qPuhN3QrT7ZZLcLCyKOSNhuijUi9G5guMRVrxq63r9YNOxxQjPm59gVxLM+7xGnHnM6cimY57tuKsjK7y9LM1g== +"@otplib/hotp@13.2.1": + version "13.2.1" + resolved "https://registry.yarnpkg.com/@otplib/hotp/-/hotp-13.2.1.tgz#6149c877e5a51fd1c527ada535fe75ca4f525eee" + integrity sha512-iRKqvj0TnemtXXtEswzBX50Z0yMNa0lH9PSdr5N4CJc1mDEuUmFFZQqnu3PfA3fPd3WeAU+mHgmK/xq18+K1QA== dependencies: - "@otplib/core" "^12.0.1" + "@otplib/core" "13.2.1" + "@otplib/uri" "13.2.1" -"@otplib/plugin-thirty-two@^12.0.1": - version "12.0.1" - resolved "https://registry.yarnpkg.com/@otplib/plugin-thirty-two/-/plugin-thirty-two-12.0.1.tgz#5cc9b56e6e89f2a1fe4a2b38900ca4e11c87aa9e" - integrity sha512-MtT+uqRso909UkbrrYpJ6XFjj9D+x2Py7KjTO9JDPhL0bJUYVu5kFP4TFZW4NFAywrAtFRxOVY261u0qwb93gA== +"@otplib/plugin-base32-scure@13.2.1": + version "13.2.1" + resolved "https://registry.yarnpkg.com/@otplib/plugin-base32-scure/-/plugin-base32-scure-13.2.1.tgz#2e7b1849311bfafd630fa87ce2812b7c3c718675" + integrity sha512-vnA2qqgJ/FbFbDNGOLAS8dKfCsJFXwFsZKYklE8yl2INkCOUR0vbVdJ2TVmufzC8R1RRZHW+cDR20ACgc9XFYg== dependencies: - "@otplib/core" "^12.0.1" - thirty-two "^1.0.2" + "@otplib/core" "13.2.1" + "@scure/base" "^2.0.0" -"@otplib/preset-default@^12.0.1": - version "12.0.1" - resolved "https://registry.yarnpkg.com/@otplib/preset-default/-/preset-default-12.0.1.tgz#cb596553c08251e71b187ada4a2246ad2a3165ba" - integrity sha512-xf1v9oOJRyXfluBhMdpOkr+bsE+Irt+0D5uHtvg6x1eosfmHCsCC6ej/m7FXiWqdo0+ZUI6xSKDhJwc8yfiOPQ== +"@otplib/plugin-crypto-noble@13.2.1": + version "13.2.1" + resolved "https://registry.yarnpkg.com/@otplib/plugin-crypto-noble/-/plugin-crypto-noble-13.2.1.tgz#11f6325f4593f57360af2268313b94003b225807" + integrity sha512-Dxjmt4L+5eDWJf5EvbcMp+fxcliyKoB9N9sNQq/vuVAUvq+KiqpiiCQZ/wHyrN0ArB0NdevtK1KByyAq080ldg== dependencies: - "@otplib/core" "^12.0.1" - "@otplib/plugin-crypto" "^12.0.1" - "@otplib/plugin-thirty-two" "^12.0.1" + "@noble/hashes" "^2.0.1" + "@otplib/core" "13.2.1" -"@otplib/preset-v11@^12.0.1": - version "12.0.1" - resolved "https://registry.yarnpkg.com/@otplib/preset-v11/-/preset-v11-12.0.1.tgz#4c7266712e7230500b421ba89252963c838fc96d" - integrity sha512-9hSetMI7ECqbFiKICrNa4w70deTUfArtwXykPUvSHWOdzOlfa9ajglu7mNCntlvxycTiOAXkQGwjQCzzDEMRMg== +"@otplib/totp@13.2.1": + version "13.2.1" + resolved "https://registry.yarnpkg.com/@otplib/totp/-/totp-13.2.1.tgz#2c8f8a8d87cf6e440d9050d363dfd24bc25179d6" + integrity sha512-LzDzAAK3w8rspF3urBnWjOlxso1SCGxX9Pnu/iy+HkC0y0HgiLsW7jhkr2hJ3u4cyBdL/tOKUhhELwsjyvunwQ== dependencies: - "@otplib/core" "^12.0.1" - "@otplib/plugin-crypto" "^12.0.1" - "@otplib/plugin-thirty-two" "^12.0.1" + "@otplib/core" "13.2.1" + "@otplib/hotp" "13.2.1" + "@otplib/uri" "13.2.1" + +"@otplib/uri@13.2.1": + version "13.2.1" + resolved "https://registry.yarnpkg.com/@otplib/uri/-/uri-13.2.1.tgz#50054fe922f3610e7558b0c5337353770c1f382e" + integrity sha512-ssYnfiUrFTs/rPRUW8h59m0MVLYOC+UKk7tVGYgtG15lLaLBrNBQjM2YFanuzn9Jm4iv9JxiNG7TRkwcnyR09A== + dependencies: + "@otplib/core" "13.2.1" "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== +"@scure/base@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-2.0.0.tgz#ba6371fddf92c2727e88ad6ab485db6e624f9a98" + integrity sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w== + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" @@ -1994,14 +2011,17 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -otplib@^12.0.1: - version "12.0.1" - resolved "https://registry.yarnpkg.com/otplib/-/otplib-12.0.1.tgz#c1d3060ab7aadf041ed2960302f27095777d1f73" - integrity sha512-xDGvUOQjop7RDgxTQ+o4pOol0/3xSZzawTiPKRrHnQWAy0WjhNs/5HdIDJCrqC4MBynmjXgULc6YfioaxZeFgg== +otplib@^13.2.1: + version "13.2.1" + resolved "https://registry.yarnpkg.com/otplib/-/otplib-13.2.1.tgz#39cc114228409ff30cfa6779e2f8cb007b535942" + integrity sha512-Cft9h/m34LtvnoB2TjP1E1E6v0biwcUntl6U4e+HgWrTa0bpwmb+u/D9gLFA+U6/ztlvrult0811Bu30nUVUuA== dependencies: - "@otplib/core" "^12.0.1" - "@otplib/preset-default" "^12.0.1" - "@otplib/preset-v11" "^12.0.1" + "@otplib/core" "13.2.1" + "@otplib/hotp" "13.2.1" + "@otplib/plugin-base32-scure" "13.2.1" + "@otplib/plugin-crypto-noble" "13.2.1" + "@otplib/totp" "13.2.1" + "@otplib/uri" "13.2.1" p-limit@^1.1.0: version "1.3.0" @@ -2856,11 +2876,6 @@ text-decoder@^1.1.0: dependencies: b4a "^1.6.4" -thirty-two@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/thirty-two/-/thirty-two-1.0.2.tgz#4ca2fffc02a51290d2744b9e3f557693ca6b627a" - integrity sha512-OEI0IWCe+Dw46019YLl6V10Us5bi574EvlJEOcAkB29IzQ/mYD1A6RyNHLjZPiHCmuodxvgF6U+vZO1L15lxVA== - tildify@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/tildify/-/tildify-2.0.0.tgz#f205f3674d677ce698b7067a99e949ce03b4754a" From f90066822fde1f479482728c9ceb22f7957bb8dc Mon Sep 17 00:00:00 2001 From: Jamie Curnow Date: Wed, 4 Feb 2026 07:47:16 +1000 Subject: [PATCH 2/2] Fix v13 otplib upgrades --- backend/internal/2fa.js | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/backend/internal/2fa.js b/backend/internal/2fa.js index aba9dd68..279f551a 100644 --- a/backend/internal/2fa.js +++ b/backend/internal/2fa.js @@ -1,6 +1,6 @@ import crypto from "node:crypto"; import bcrypt from "bcrypt"; -import { authenticator } from "otplib"; +import { generateSecret, generateURI, verify } from "otplib"; import errs from "../lib/error.js"; import authModel from "../models/auth.js"; import internalUser from "./user.js"; @@ -27,7 +27,6 @@ const generateBackupCodes = async () => { }; const internal2fa = { - /** * Check if user has 2FA enabled * @param {number} userId @@ -72,8 +71,12 @@ const internal2fa = { startSetup: async (access, userId) => { await access.can("users:password", userId); const user = await internalUser.get(access, { id: userId }); - const secret = authenticator.generateSecret(); - const otpauth_url = authenticator.keyuri(user.email, APP_NAME, secret); + const secret = generateSecret(); + const otpauth_url = generateURI({ + issuer: APP_NAME, + label: user.email, + secret: secret, + }); const auth = await internal2fa.getUserPasswordAuth(userId); // ensure user isn't already setup for 2fa @@ -85,7 +88,8 @@ const internal2fa = { const meta = auth.meta || {}; meta.totp_pending_secret = secret; - await authModel.query() + await authModel + .query() .where("id", auth.id) .andWhere("user_id", userId) .andWhere("type", "password") @@ -112,8 +116,8 @@ const internal2fa = { throw new errs.ValidationError("No pending 2FA setup found"); } - const valid = authenticator.verify({ token: code, secret }); - if (!valid) { + const result = await verify({ token: code, secret }); + if (!result.valid) { throw new errs.ValidationError("Invalid verification code"); } @@ -156,12 +160,12 @@ const internal2fa = { throw new errs.ValidationError("2FA is not enabled"); } - const valid = authenticator.verify({ + const result = await verify({ token: code, secret: auth.meta.totp_secret, }); - if (!valid) { + if (!result.valid) { throw new errs.AuthError("Invalid verification code"); } @@ -195,12 +199,12 @@ const internal2fa = { } // Try TOTP code first - const valid = authenticator.verify({ + const result = await verify({ token, secret, }); - if (valid) { + if (result.valid) { return true; } @@ -248,12 +252,12 @@ const internal2fa = { throw new errs.ValidationError("No 2FA secret found"); } - const valid = authenticator.verify({ + const result = await verify({ token, secret, }); - if (!valid) { + if (!result.valid) { throw new errs.ValidationError("Invalid verification code"); } @@ -271,11 +275,7 @@ const internal2fa = { }, getUserPasswordAuth: async (userId) => { - const auth = await authModel - .query() - .where("user_id", userId) - .andWhere("type", "password") - .first(); + const auth = await authModel.query().where("user_id", userId).andWhere("type", "password").first(); if (!auth) { throw new errs.ItemNotFoundError("Auth not found");