From 1376b07a4ba2f9d35176640ad381155749dac408 Mon Sep 17 00:00:00 2001 From: Vishal4real Date: Wed, 11 Feb 2026 23:27:34 +0530 Subject: [PATCH 1/2] fix: disable MFA without TOTP --- package-lock.json | 64 +++++++++++++++++++++++++---- spec/AuthenticationAdapters.spec.js | 58 +++++++++++++++++++++++++- src/Adapters/Auth/AuthAdapter.js | 11 +++++ src/Adapters/Auth/index.js | 11 +++++ src/Adapters/Auth/mfa.js | 4 ++ src/Auth.js | 16 ++++++-- 6 files changed, 151 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index c688a3c228..1e5f88c93e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -163,6 +163,7 @@ "integrity": "sha512-YM9lQpm0VfVco4DSyKooHS/fDTiKQcCHfxr7i3iL6a0kP/jNO5+4NFK6vtRDxaYisd5BrwOZHLJpPBnvRVpKPg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@wry/caches": "^1.0.0", @@ -524,6 +525,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -3797,6 +3799,7 @@ "integrity": "sha512-ODsoD39Lq6vR6aBgvjTnA3nZGliknKboc9Gtxr7E4WDNqY24MxANKcuDQSF0jzapvGb3KWOEDrKfve4HoWGK+g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.1", @@ -4232,6 +4235,7 @@ "version": "5.10.0", "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.10.0.tgz", "integrity": "sha512-JXmM4XCoso6C75Mr3lhKA3eNxSzkYi3nCzxDIKY+YOszYsJjuKbFgVtguVPbLMOttN4iu2fXoc2BGhdnYhIOxA==", + "peer": true, "dependencies": { "cluster-key-slot": "1.1.2" }, @@ -4300,6 +4304,7 @@ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.0.tgz", "integrity": "sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg==", "dev": true, + "peer": true, "dependencies": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.1.0", @@ -5012,6 +5017,7 @@ "resolved": "https://registry.npmjs.org/marked/-/marked-9.1.6.tgz", "integrity": "sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==", "dev": true, + "peer": true, "bin": { "marked": "bin/marked.js" }, @@ -5131,6 +5137,7 @@ "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-22.0.12.tgz", "integrity": "sha512-0mhiCR/4sZb00RVFJIUlMuiBkW3NMpVIW2Gse7noqEMoFGkvfPPAImEQbkBV8xga4KOPP4FdTRYuLLy32R1fPw==", "dev": true, + "peer": true, "dependencies": { "@semantic-release/commit-analyzer": "^11.0.0", "@semantic-release/error": "^4.0.0", @@ -6057,6 +6064,7 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "peer": true, "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", @@ -6118,6 +6126,7 @@ "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.1.tgz", "integrity": "sha512-4NpsnpYl2Gt1ljyBGrKMxFYAYvpqbnnkgP/i/g+NLpjEUa3obn1XJCur9YbEXKDAkaXqsR1LbDnGEJ0MmKFxfg==", "dev": true, + "peer": true, "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" @@ -6250,6 +6259,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.53.1.tgz", "integrity": "sha512-cFYYFZ+oQFi6hUnBTbLRXfTJiaQtYE3t4O692agbBl+2Zy+eqSKWtPjhPXJu1G7j4RLjKgeJPDdq3EqOwmX5Ag==", "dev": true, + "peer": true, "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.53.1", @@ -6342,6 +6352,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.53.1.tgz", "integrity": "sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==", "dev": true, + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.53.1", "@typescript-eslint/types": "8.53.1", @@ -7133,6 +7144,7 @@ "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -7692,6 +7704,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", @@ -9841,6 +9854,7 @@ "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -10302,6 +10316,7 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -10345,6 +10360,7 @@ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", "license": "MIT", + "peer": true, "engines": { "node": ">= 16" }, @@ -11710,6 +11726,7 @@ "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.11.0.tgz", "integrity": "sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==", "license": "MIT", + "peer": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } @@ -12861,6 +12878,7 @@ "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.4.tgz", "integrity": "sha512-zeFezwyXeG4syyYHbvh1A967IAqq/67yXtXvuL5wnqCkFZe8I0vKfm+EO+YEvLguo6w9CDUbrAXVtJSHh2E8rw==", "dev": true, + "peer": true, "dependencies": { "@babel/parser": "^7.20.15", "@jsdoc/salty": "^0.2.1", @@ -14041,6 +14059,7 @@ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", "dev": true, + "peer": true, "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", @@ -14068,6 +14087,7 @@ "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", "dev": true, + "peer": true, "bin": { "marked": "bin/marked.js" }, @@ -18320,6 +18340,7 @@ "resolved": "https://registry.npmjs.org/pg/-/pg-8.18.0.tgz", "integrity": "sha512-xqrUDL1b9MbkydY/s+VZ6v+xiMUmOUk7SS9d/1kpyQxoJ6U9AO1oIJyUWVZojbfe5Cc/oluutcgFG4L9RDP1iQ==", "license": "MIT", + "peer": true, "dependencies": { "pg-connection-string": "^2.11.0", "pg-pool": "^3.11.0", @@ -18360,7 +18381,6 @@ "resolved": "https://registry.npmjs.org/pg-cursor/-/pg-cursor-2.17.0.tgz", "integrity": "sha512-2Uio3Xfl5ldwJfls+RgGL+YbPcKQncWACWjYQFqlamvHZ4HJFjZhhZBbqd7jQ2LIkZYSvU90bm2dNW0rno+QFQ==", "license": "MIT", - "peer": true, "peerDependencies": { "pg": "^8" } @@ -18643,6 +18663,7 @@ "url": "https://github.com/sponsors/ai" } ], + "peer": true, "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.1.0", @@ -19648,6 +19669,7 @@ "integrity": "sha512-9xV49HNY8C0/WmPWxTlaNleiXhWb//qfMzG2c5X8/k7tuWcu8RssbuS+sujb/h7PiWSXv53mrQvV9hrO9b7vuQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@semantic-release/commit-analyzer": "^13.0.0-beta.1", "@semantic-release/error": "^4.0.0", @@ -21254,6 +21276,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, + "peer": true, "engines": { "node": ">=12" }, @@ -21517,6 +21540,7 @@ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -22605,6 +22629,7 @@ "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.13.8.tgz", "integrity": "sha512-YM9lQpm0VfVco4DSyKooHS/fDTiKQcCHfxr7i3iL6a0kP/jNO5+4NFK6vtRDxaYisd5BrwOZHLJpPBnvRVpKPg==", "dev": true, + "peer": true, "requires": { "@graphql-typed-document-node/core": "^3.1.1", "@wry/caches": "^1.0.0", @@ -22843,6 +22868,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, + "peer": true, "requires": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -25038,6 +25064,7 @@ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.2.tgz", "integrity": "sha512-ODsoD39Lq6vR6aBgvjTnA3nZGliknKboc9Gtxr7E4WDNqY24MxANKcuDQSF0jzapvGb3KWOEDrKfve4HoWGK+g==", "dev": true, + "peer": true, "requires": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.1", @@ -25404,6 +25431,7 @@ "version": "5.10.0", "resolved": "https://registry.npmjs.org/@redis/client/-/client-5.10.0.tgz", "integrity": "sha512-JXmM4XCoso6C75Mr3lhKA3eNxSzkYi3nCzxDIKY+YOszYsJjuKbFgVtguVPbLMOttN4iu2fXoc2BGhdnYhIOxA==", + "peer": true, "requires": { "cluster-key-slot": "1.1.2" } @@ -25451,6 +25479,7 @@ "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.0.tgz", "integrity": "sha512-1LFfa/qnMQvEOAdzlQymH0ulepxbxnCYAKJZfMci/5XJyIHWgEYnDmgnKakbTh7CH2tFQ5O60oYDvns4i9RAIg==", "dev": true, + "peer": true, "requires": { "@octokit/auth-token": "^4.0.0", "@octokit/graphql": "^7.1.0", @@ -25960,7 +25989,8 @@ "version": "9.1.6", "resolved": "https://registry.npmjs.org/marked/-/marked-9.1.6.tgz", "integrity": "sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==", - "dev": true + "dev": true, + "peer": true }, "marked-terminal": { "version": "6.2.0", @@ -26029,6 +26059,7 @@ "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-22.0.12.tgz", "integrity": "sha512-0mhiCR/4sZb00RVFJIUlMuiBkW3NMpVIW2Gse7noqEMoFGkvfPPAImEQbkBV8xga4KOPP4FdTRYuLLy32R1fPw==", "dev": true, + "peer": true, "requires": { "@semantic-release/commit-analyzer": "^11.0.0", "@semantic-release/error": "^4.0.0", @@ -26644,6 +26675,7 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "peer": true, "requires": { "@types/body-parser": "*", "@types/express-serve-static-core": "^4.17.33", @@ -26703,6 +26735,7 @@ "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.1.tgz", "integrity": "sha512-4NpsnpYl2Gt1ljyBGrKMxFYAYvpqbnnkgP/i/g+NLpjEUa3obn1XJCur9YbEXKDAkaXqsR1LbDnGEJ0MmKFxfg==", "dev": true, + "peer": true, "requires": { "@types/linkify-it": "^5", "@types/mdurl": "^2" @@ -26831,6 +26864,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.53.1.tgz", "integrity": "sha512-cFYYFZ+oQFi6hUnBTbLRXfTJiaQtYE3t4O692agbBl+2Zy+eqSKWtPjhPXJu1G7j4RLjKgeJPDdq3EqOwmX5Ag==", "dev": true, + "peer": true, "requires": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.53.1", @@ -26884,6 +26918,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.53.1.tgz", "integrity": "sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==", "dev": true, + "peer": true, "requires": { "@typescript-eslint/scope-manager": "8.53.1", "@typescript-eslint/types": "8.53.1", @@ -27395,7 +27430,8 @@ "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", - "dev": true + "dev": true, + "peer": true }, "acorn-jsx": { "version": "5.3.2", @@ -27805,6 +27841,7 @@ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "dev": true, + "peer": true, "requires": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", @@ -29319,6 +29356,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz", "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==", "dev": true, + "peer": true, "requires": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -29632,6 +29670,7 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "peer": true, "requires": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -29682,6 +29721,7 @@ "version": "7.5.1", "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", + "peer": true, "requires": {} }, "extend": { @@ -30615,7 +30655,8 @@ "graphql": { "version": "16.11.0", "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.11.0.tgz", - "integrity": "sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==" + "integrity": "sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==", + "peer": true }, "graphql-list-fields": { "version": "2.0.4", @@ -31424,6 +31465,7 @@ "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-4.0.4.tgz", "integrity": "sha512-zeFezwyXeG4syyYHbvh1A967IAqq/67yXtXvuL5wnqCkFZe8I0vKfm+EO+YEvLguo6w9CDUbrAXVtJSHh2E8rw==", "dev": true, + "peer": true, "requires": { "@babel/parser": "^7.20.15", "@jsdoc/salty": "^0.2.1", @@ -32303,6 +32345,7 @@ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", "dev": true, + "peer": true, "requires": { "argparse": "^2.0.1", "entities": "^4.4.0", @@ -32323,7 +32366,8 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", - "dev": true + "dev": true, + "peer": true }, "marked-terminal": { "version": "7.3.0", @@ -35181,6 +35225,7 @@ "version": "8.18.0", "resolved": "https://registry.npmjs.org/pg/-/pg-8.18.0.tgz", "integrity": "sha512-xqrUDL1b9MbkydY/s+VZ6v+xiMUmOUk7SS9d/1kpyQxoJ6U9AO1oIJyUWVZojbfe5Cc/oluutcgFG4L9RDP1iQ==", + "peer": true, "requires": { "pg-cloudflare": "^1.3.0", "pg-connection-string": "^2.11.0", @@ -35205,7 +35250,6 @@ "version": "2.17.0", "resolved": "https://registry.npmjs.org/pg-cursor/-/pg-cursor-2.17.0.tgz", "integrity": "sha512-2Uio3Xfl5ldwJfls+RgGL+YbPcKQncWACWjYQFqlamvHZ4HJFjZhhZBbqd7jQ2LIkZYSvU90bm2dNW0rno+QFQ==", - "peer": true, "requires": {} }, "pg-int8": { @@ -35391,6 +35435,7 @@ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "dev": true, + "peer": true, "requires": { "nanoid": "^3.3.7", "picocolors": "^1.1.0", @@ -36101,6 +36146,7 @@ "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.2.5.tgz", "integrity": "sha512-9xV49HNY8C0/WmPWxTlaNleiXhWb//qfMzG2c5X8/k7tuWcu8RssbuS+sujb/h7PiWSXv53mrQvV9hrO9b7vuQ==", "dev": true, + "peer": true, "requires": { "@semantic-release/commit-analyzer": "^13.0.0-beta.1", "@semantic-release/error": "^4.0.0", @@ -37227,7 +37273,8 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true + "dev": true, + "peer": true } } }, @@ -37412,7 +37459,8 @@ "version": "5.8.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true + "dev": true, + "peer": true }, "typescript-eslint": { "version": "8.53.1", diff --git a/spec/AuthenticationAdapters.spec.js b/spec/AuthenticationAdapters.spec.js index a2defde3e5..72078eb6cb 100644 --- a/spec/AuthenticationAdapters.spec.js +++ b/spec/AuthenticationAdapters.spec.js @@ -446,8 +446,8 @@ describe('AuthenticationProviders', function () { it('properly loads a custom adapter with options', () => { const options = { custom: { - validateAppId: () => {}, - validateAuthData: () => {}, + validateAppId: () => { }, + validateAuthData: () => { }, appIds: ['a', 'b'], }, }; @@ -1696,6 +1696,60 @@ describe('OTP TOTP auth adatper', () => { }) ).toBeRejectedWith({ code: Parse.Error.SCRIPT_FAILED, error: 'Invalid MFA token' }); }); + + it('cannot unlink MFA via null', async () => { + const user = await Parse.User.signUp('username', 'password'); + const OTPAuth = require('otpauth'); + const secret = new OTPAuth.Secret(); + const totp = new OTPAuth.TOTP({ + algorithm: 'SHA1', + digits: 6, + period: 30, + secret, + }); + const token = totp.generate(); + await user.save( + { authData: { mfa: { secret: secret.base32, token } } }, + { sessionToken: user.getSessionToken() } + ); + + await user.fetch(); + expect(user.get('authData').mfa).toEqual({ status: 'enabled' }); + + await expectAsync( + user.save({ authData: { mfa: null } }, { sessionToken: user.getSessionToken() }) + ).toBeRejected(); + + await user.fetch(); + expect(user.get('authData').mfa).toBeDefined(); + expect(user.get('authData').mfa.status).toBe('enabled'); + }); + + it('cannot unlink MFA via _unlinkFrom', async () => { + const user = await Parse.User.signUp('username', 'password'); + const OTPAuth = require('otpauth'); + const secret = new OTPAuth.Secret(); + const totp = new OTPAuth.TOTP({ + algorithm: 'SHA1', + digits: 6, + period: 30, + secret, + }); + const token = totp.generate(); + await user.save( + { authData: { mfa: { secret: secret.base32, token } } }, + { sessionToken: user.getSessionToken() } + ); + + await user.fetch(); + expect(user.get('authData').mfa).toEqual({ status: 'enabled' }); + + await expectAsync(user._unlinkFrom('mfa')).toBeRejected(); + + await user.fetch(); + expect(user.get('authData').mfa).toBeDefined(); + expect(user.get('authData').mfa.status).toBe('enabled'); + }); }); describe('OTP SMS auth adatper', () => { diff --git a/src/Adapters/Auth/AuthAdapter.js b/src/Adapters/Auth/AuthAdapter.js index ebcea4d3d8..4b29836802 100644 --- a/src/Adapters/Auth/AuthAdapter.js +++ b/src/Adapters/Auth/AuthAdapter.js @@ -84,6 +84,17 @@ export class AuthAdapter { return Promise.resolve({}); } + /** + * Triggered when user tries to remove authData related to this provider + * @param {Object} authData The client provided authData (usually null) + * @param {Object} options additional adapter options + * @param {Parse.Cloud.TriggerRequest} request + * @returns {Promise} + */ + validateUnlink(authData, options, req) { + return Promise.resolve({}); + } + /** * Triggered when user is looked up by authData with this provider. Override the `id` field if needed. * @param {Object} authData The client provided authData diff --git a/src/Adapters/Auth/index.js b/src/Adapters/Auth/index.js index 7f5581da49..e69f44b098 100755 --- a/src/Adapters/Auth/index.js +++ b/src/Adapters/Auth/index.js @@ -110,6 +110,16 @@ function authDataValidator(provider, adapter, appIds, options) { hasAuthDataConfigured = true; } + if (authData === null && isLoggedIn && hasAuthDataConfigured) { + if (typeof adapter.validateUnlink === 'function') { + return { + method: 'validateUnlink', + validator: () => adapter.validateUnlink(authData, options, requestObject), + }; + } + return; + } + if (isLoggedIn) { // User is updating their authData if (hasAuthDataConfigured) { @@ -169,6 +179,7 @@ function loadAuthAdapter(provider, authOptions) { 'validateSetUp', 'validateLogin', 'validateUpdate', + 'validateUnlink', 'challenge', 'validateOptions', 'policy', diff --git a/src/Adapters/Auth/mfa.js b/src/Adapters/Auth/mfa.js index df2fa73d02..91f2fdfe4d 100644 --- a/src/Adapters/Auth/mfa.js +++ b/src/Adapters/Auth/mfa.js @@ -191,6 +191,10 @@ class MFAAdapter extends AuthAdapter { } throw 'Invalid MFA data'; } + validateUnlink() { + throw 'Invalid MFA data'; + } + afterFind(authData, options, req) { if (req.master) { return; diff --git a/src/Auth.js b/src/Auth.js index 0601151ca4..6762e9836b 100644 --- a/src/Auth.js +++ b/src/Auth.js @@ -570,8 +570,18 @@ const handleAuthDataValidation = async (authData, req, foundUser) => { let method = ''; try { if (authData[provider] === null) { - acc.authData[provider] = null; - continue; + let authAdapter; + try { + authAdapter = req.config.authDataManager.getValidatorForProvider(provider); + } catch (e) { + // Ignore error + } + const { adapter } = authAdapter || {}; + + if (!adapter || typeof adapter.validateUnlink !== 'function') { + acc.authData[provider] = null; + continue; + } } const { validator } = req.config.authDataManager.getValidatorForProvider(provider) || {}; const authProvider = (req.config.auth || {})[provider] || {}; @@ -612,7 +622,7 @@ const handleAuthDataValidation = async (authData, req, foundUser) => { req.auth && req.auth.user ? req.auth.user.id : req.data.objectId || undefined; logger.error( `Failed running auth step ${method} for ${provider} for user ${userString} with Error: ` + - JSON.stringify(e), + JSON.stringify(e), { authenticationStep: method, error: e, From b303a7ffc8dc5849244ff3f6de51ec8d3de7ca32 Mon Sep 17 00:00:00 2001 From: Vishal4real Date: Thu, 12 Feb 2026 00:25:24 +0530 Subject: [PATCH 2/2] chore(auth): improve error logging for auth adapter loading and allow master key MFA unlink --- spec/AuthenticationAdapters.spec.js | 24 ++++++++++++++++++++++++ src/Adapters/Auth/mfa.js | 5 ++++- src/Auth.js | 2 +- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/spec/AuthenticationAdapters.spec.js b/spec/AuthenticationAdapters.spec.js index 72078eb6cb..b044e08cbd 100644 --- a/spec/AuthenticationAdapters.spec.js +++ b/spec/AuthenticationAdapters.spec.js @@ -1874,4 +1874,28 @@ describe('OTP SMS auth adatper', () => { await Parse.User.logIn('username', 'password'); expect(spy).not.toHaveBeenCalled(); }); + it('should allow unlinking MFA with master key', async () => { + const user = await Parse.User.signUp('username_mfa_unlink_integrated', 'password'); + const sessionToken = user.getSessionToken(); + + // Enroll in MFA + await user.save({ authData: { mfa: { mobile: '+11111111111' } } }, { sessionToken }); + + // Confirm enrollment + await user.save({ authData: { mfa: { mobile: '+11111111111', token: code } } }, { sessionToken }); + await user.fetch({ sessionToken }); + expect(user.get('authData').mfa).toBeDefined(); + + // Now try to unlink with Master Key + await user.save({ authData: { mfa: null } }, { useMasterKey: true }); + + // Verification + await user.fetch({ useMasterKey: true }); + const authData = user.get('authData'); + if (authData) { + expect(authData.mfa).toBeUndefined(); + } else { + expect(authData).toBeUndefined(); + } + }); }); diff --git a/src/Adapters/Auth/mfa.js b/src/Adapters/Auth/mfa.js index 91f2fdfe4d..ca4019a198 100644 --- a/src/Adapters/Auth/mfa.js +++ b/src/Adapters/Auth/mfa.js @@ -191,7 +191,10 @@ class MFAAdapter extends AuthAdapter { } throw 'Invalid MFA data'; } - validateUnlink() { + validateUnlink(authData, options, req) { + if (req.master) { + return; + } throw 'Invalid MFA data'; } diff --git a/src/Auth.js b/src/Auth.js index 6762e9836b..d2d91d73b9 100644 --- a/src/Auth.js +++ b/src/Auth.js @@ -574,7 +574,7 @@ const handleAuthDataValidation = async (authData, req, foundUser) => { try { authAdapter = req.config.authDataManager.getValidatorForProvider(provider); } catch (e) { - // Ignore error + logger.debug(`Failed to load auth adapter for provider ${provider} during unlinking: ${e}`); } const { adapter } = authAdapter || {};