From 6e704c7c0a33aba82f68cbc793d7645ac8f03059 Mon Sep 17 00:00:00 2001 From: Artem Niehrieiev Date: Wed, 20 May 2026 14:59:48 +0000 Subject: [PATCH] feat: upgrade otplib to version 13.4.0 and refactor OTP handling methods --- backend/package.json | 2 +- .../user/use-cases/disable-otp.use.case.ts | 4 +- .../user/use-cases/generate-otp-use.case.ts | 4 +- .../user/use-cases/otp-login-use.case.ts | 4 +- .../user/use-cases/verify-otp-use.case.ts | 4 +- .../entities/user/utils/generate-qr-code.ts | 4 +- pnpm-lock.yaml | 94 +++++++++++-------- 7 files changed, 64 insertions(+), 52 deletions(-) diff --git a/backend/package.json b/backend/package.json index 4a49bc4f3..d88ca82d3 100644 --- a/backend/package.json +++ b/backend/package.json @@ -82,7 +82,7 @@ "node-sql-parser": "^5.4.0", "nodemailer": "^8.0.7", "nunjucks": "^3.2.4", - "otplib": "^12.0.1", + "otplib": "^13.4.0", "p-queue": "9.3.0", "pg-connection-string": "^2.13.0", "qrcode": "^1.5.4", diff --git a/backend/src/entities/user/use-cases/disable-otp.use.case.ts b/backend/src/entities/user/use-cases/disable-otp.use.case.ts index c2a06a2f9..b9f3b8bbe 100644 --- a/backend/src/entities/user/use-cases/disable-otp.use.case.ts +++ b/backend/src/entities/user/use-cases/disable-otp.use.case.ts @@ -6,7 +6,7 @@ import { InternalServerErrorException, NotFoundException, } from '@nestjs/common'; -import { authenticator } from 'otplib'; +import { verifySync } from 'otplib'; import AbstractUseCase from '../../../common/abstract-use.case.js'; import { IGlobalDatabaseContext } from '../../../common/application/global-database-context.interface.js'; import { BaseType } from '../../../common/data-injection.tokens.js'; @@ -41,7 +41,7 @@ export class DisableOtpUseCase extends AbstractUseCase imp ); } - const otpSecretKey = authenticator.generateSecret(); + const otpSecretKey = generateSecret(); foundUser.otpSecretKey = otpSecretKey; await this._dbContext.userRepository.saveUserEntity(foundUser); const { otpauth, qrCode } = await generateQRCode(foundUser.email, otpSecretKey); diff --git a/backend/src/entities/user/use-cases/otp-login-use.case.ts b/backend/src/entities/user/use-cases/otp-login-use.case.ts index 1e9e9dafc..9ce4720e1 100644 --- a/backend/src/entities/user/use-cases/otp-login-use.case.ts +++ b/backend/src/entities/user/use-cases/otp-login-use.case.ts @@ -1,5 +1,5 @@ import { BadRequestException, Inject, Injectable, NotFoundException } from '@nestjs/common'; -import { authenticator } from 'otplib'; +import { verifySync } from 'otplib'; import AbstractUseCase from '../../../common/abstract-use.case.js'; import { IGlobalDatabaseContext } from '../../../common/application/global-database-context.interface.js'; import { BaseType } from '../../../common/data-injection.tokens.js'; @@ -28,7 +28,7 @@ export class OtpLoginUseCase extends AbstractUseCase implem if (!foundUser) { throw new NotFoundException(Messages.USER_NOT_FOUND); } - const isValid = authenticator.check(otpToken, foundUser.otpSecretKey); + const isValid = verifySync({ token: otpToken, secret: foundUser.otpSecretKey }).valid; if (!isValid) { await this.recordSignInAudit( foundUser.email, diff --git a/backend/src/entities/user/use-cases/verify-otp-use.case.ts b/backend/src/entities/user/use-cases/verify-otp-use.case.ts index 753d64457..b0c3b626e 100644 --- a/backend/src/entities/user/use-cases/verify-otp-use.case.ts +++ b/backend/src/entities/user/use-cases/verify-otp-use.case.ts @@ -1,5 +1,5 @@ import { HttpException, HttpStatus, Inject, Injectable } from '@nestjs/common'; -import { authenticator } from 'otplib'; +import { verifySync } from 'otplib'; import AbstractUseCase from '../../../common/abstract-use.case.js'; import { IGlobalDatabaseContext } from '../../../common/application/global-database-context.interface.js'; import { BaseType } from '../../../common/data-injection.tokens.js'; @@ -48,7 +48,7 @@ export class VerifyOtpUseCase extends AbstractUseCase=16} + '@noble/hashes@2.2.0': + resolution: {integrity: sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==} + engines: {node: '>= 20.19.0'} + '@nodable/entities@2.1.0': resolution: {integrity: sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==} @@ -1696,23 +1700,23 @@ packages: peerDependencies: '@opentelemetry/api': ^1.1.0 - '@otplib/core@12.0.1': - resolution: {integrity: sha512-4sGntwbA/AC+SbPhbsziRiD+jNDdIzsZ3JUyfZwjtKyc/wufl1pnSIaG4Uqx8ymPagujub0o92kgBnB89cuAMA==} + '@otplib/core@13.4.0': + resolution: {integrity: sha512-JqOGcvZQi2wIkEQo8f3/iAjstavpXy6gouIDMHygjNuH6Q0FjbHOiXMdcE94RwfgDNMABhzwUmvaPsxvgm9NYw==} + + '@otplib/hotp@13.4.0': + resolution: {integrity: sha512-MJjE0x06mn2ptymz5qZmQveb+vWFuaIftqE0b5/TZZqUOK7l97cV8lRTmid5BpAQMwJDNLW6RnYxGeCRiNdekw==} - '@otplib/plugin-crypto@12.0.1': - resolution: {integrity: sha512-qPuhN3QrT7ZZLcLCyKOSNhuijUi9G5guMRVrxq63r9YNOxxQjPm59gVxLM+7xGnHnM6cimY57tuKsjK7y9LM1g==} - deprecated: Please upgrade to v13 of otplib. Refer to otplib docs for migration paths + '@otplib/plugin-base32-scure@13.4.0': + resolution: {integrity: sha512-/t9YWJmMbB8bF5z8mXrBZc2FXBe8B/3hG5FhWr9K8cFwFhyxScbPysmZe8s1UTzSA6N+s8Uv8aIfCtVXPNjJWw==} - '@otplib/plugin-thirty-two@12.0.1': - resolution: {integrity: sha512-MtT+uqRso909UkbrrYpJ6XFjj9D+x2Py7KjTO9JDPhL0bJUYVu5kFP4TFZW4NFAywrAtFRxOVY261u0qwb93gA==} - deprecated: Please upgrade to v13 of otplib. Refer to otplib docs for migration paths + '@otplib/plugin-crypto-noble@13.4.0': + resolution: {integrity: sha512-KrvE4m7Zv+TT1944HzgqFJWJpKb6AyoxDbvhPStmBqdMlv5Gekb80d66cuFRL08kkPgJ5gXUSb5SFpYeB+bACg==} - '@otplib/preset-default@12.0.1': - resolution: {integrity: sha512-xf1v9oOJRyXfluBhMdpOkr+bsE+Irt+0D5uHtvg6x1eosfmHCsCC6ej/m7FXiWqdo0+ZUI6xSKDhJwc8yfiOPQ==} - deprecated: Please upgrade to v13 of otplib. Refer to otplib docs for migration paths + '@otplib/totp@13.4.0': + resolution: {integrity: sha512-dK+vl0f0ekzf6mCENRI9AKS2NJUC7OjI3+X8e7QSnhQ2WM7I+i4PGpb3QxKi5hxjTtwVuoZwXR2CFtXdcRtNdQ==} - '@otplib/preset-v11@12.0.1': - resolution: {integrity: sha512-9hSetMI7ECqbFiKICrNa4w70deTUfArtwXykPUvSHWOdzOlfa9ajglu7mNCntlvxycTiOAXkQGwjQCzzDEMRMg==} + '@otplib/uri@13.4.0': + resolution: {integrity: sha512-x1ozBa5bPbdZCrrTL/HK21qchiK7jYElTu+0ft22abeEhiLYgH1+SIULvOcVk3CK8YwF4kdcidvkq4ciejucJA==} '@oxc-parser/binding-android-arm-eabi@0.130.0': resolution: {integrity: sha512-h/xYU8/7ADWzVSf5I+YalLpj33LOy9CI/zgbJNIZ5eunRBG+Czqa3lZsvuPHHf3rOt6z1c5+UzoxjbAzAvhwVw==} @@ -2016,6 +2020,9 @@ packages: '@scarf/scarf@1.4.0': resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==} + '@scure/base@2.2.0': + resolution: {integrity: sha512-b8XEupJibegiXV+tDUseI8oLQc8ei3d/4Jkb2RpbHh3MfE054ov3uIz2dhFkB3FI8iwYkEh0gGCApkrYggkPNg==} + '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} @@ -4483,8 +4490,8 @@ packages: resolution: {integrity: sha512-kGUumXmrEWbSpBuKJyb9Ip3rXcNgKK6grunI3/cLPzrRvboZ6ZoLi9JQ+z6M/RIG924tY8BLflihL4CKKQAYMA==} engines: {node: '>=14.17'} - otplib@12.0.1: - resolution: {integrity: sha512-xDGvUOQjop7RDgxTQ+o4pOol0/3xSZzawTiPKRrHnQWAy0WjhNs/5HdIDJCrqC4MBynmjXgULc6YfioaxZeFgg==} + otplib@13.4.0: + resolution: {integrity: sha512-RUcYcRMCgRWhUE/XabRppXpUwCwaWBNHe5iPXhdvP8wwDGpGpsIf/kxX/ec3zFsOaM1Oq8lEhUqDwk6W7DHkwg==} outvariant@1.4.3: resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} @@ -5194,10 +5201,6 @@ packages: text-hex@1.0.0: resolution: {integrity: sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==} - thirty-two@1.0.2: - resolution: {integrity: sha512-OEI0IWCe+Dw46019YLl6V10Us5bi574EvlJEOcAkB29IzQ/mYD1A6RyNHLjZPiHCmuodxvgF6U+vZO1L15lxVA==} - engines: {node: '>=0.2.6'} - tildify@2.0.0: resolution: {integrity: sha512-Cc+OraorugtXNfs50hU9KS369rFXCfgGLpfCfvlc+Ud5u6VWmUQsOAa9HbTvheQdYnrdJqqv1e5oIqXppMYnSw==} engines: {node: '>=8'} @@ -7243,6 +7246,8 @@ snapshots: '@noble/hashes@1.8.0': {} + '@noble/hashes@2.2.0': {} + '@nodable/entities@2.1.0': {} '@nodelib/fs.scandir@2.1.5': @@ -7512,28 +7517,32 @@ snapshots: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.7.0(@opentelemetry/api@1.9.1) - '@otplib/core@12.0.1': {} + '@otplib/core@13.4.0': {} - '@otplib/plugin-crypto@12.0.1': + '@otplib/hotp@13.4.0': dependencies: - '@otplib/core': 12.0.1 + '@otplib/core': 13.4.0 + '@otplib/uri': 13.4.0 - '@otplib/plugin-thirty-two@12.0.1': + '@otplib/plugin-base32-scure@13.4.0': dependencies: - '@otplib/core': 12.0.1 - thirty-two: 1.0.2 + '@otplib/core': 13.4.0 + '@scure/base': 2.2.0 - '@otplib/preset-default@12.0.1': + '@otplib/plugin-crypto-noble@13.4.0': dependencies: - '@otplib/core': 12.0.1 - '@otplib/plugin-crypto': 12.0.1 - '@otplib/plugin-thirty-two': 12.0.1 + '@noble/hashes': 2.2.0 + '@otplib/core': 13.4.0 - '@otplib/preset-v11@12.0.1': + '@otplib/totp@13.4.0': dependencies: - '@otplib/core': 12.0.1 - '@otplib/plugin-crypto': 12.0.1 - '@otplib/plugin-thirty-two': 12.0.1 + '@otplib/core': 13.4.0 + '@otplib/hotp': 13.4.0 + '@otplib/uri': 13.4.0 + + '@otplib/uri@13.4.0': + dependencies: + '@otplib/core': 13.4.0 '@oxc-parser/binding-android-arm-eabi@0.130.0': optional: true @@ -7712,6 +7721,8 @@ snapshots: '@scarf/scarf@1.4.0': {} + '@scure/base@2.2.0': {} + '@sec-ant/readable-stream@0.4.1': {} '@sentry/core@10.53.1': {} @@ -10405,11 +10416,14 @@ snapshots: oracledb@6.10.0: {} - otplib@12.0.1: + otplib@13.4.0: dependencies: - '@otplib/core': 12.0.1 - '@otplib/preset-default': 12.0.1 - '@otplib/preset-v11': 12.0.1 + '@otplib/core': 13.4.0 + '@otplib/hotp': 13.4.0 + '@otplib/plugin-base32-scure': 13.4.0 + '@otplib/plugin-crypto-noble': 13.4.0 + '@otplib/totp': 13.4.0 + '@otplib/uri': 13.4.0 outvariant@1.4.3: {} @@ -11183,8 +11197,6 @@ snapshots: text-hex@1.0.0: {} - thirty-two@1.0.2: {} - tildify@2.0.0: {} time-zone@1.0.0: {}