diff --git a/examples/for-tests/src/App.js b/examples/for-tests/src/App.js
index f65dac002..6273474b2 100644
--- a/examples/for-tests/src/App.js
+++ b/examples/for-tests/src/App.js
@@ -14,6 +14,7 @@ import Multitenancy from "supertokens-auth-react/recipe/multitenancy";
import ThirdPartyEmailPassword from "supertokens-auth-react/recipe/thirdpartyemailpassword";
import ThirdPartyPasswordless from "supertokens-auth-react/recipe/thirdpartypasswordless";
import UserRoles from "supertokens-auth-react/recipe/userroles";
+import MultiFactorAuth from "supertokens-auth-react/recipe/multifactorauth";
import axios from "axios";
import { useSessionContext } from "supertokens-auth-react/recipe/session";
@@ -171,6 +172,9 @@ const formFields = [
const testContext = getTestContext();
let recipeList = [
+ MultiFactorAuth.init({
+ firstFactors: ["emailpassword", "thirdparty"],
+ }),
Multitenancy.init({
override: {
functions: (oI) => ({
diff --git a/examples/for-tests/src/testContext.js b/examples/for-tests/src/testContext.js
index 618e990ba..ab1e6e3c5 100644
--- a/examples/for-tests/src/testContext.js
+++ b/examples/for-tests/src/testContext.js
@@ -23,7 +23,7 @@ export function getEnabledRecipes() {
let enabledRecipes = [];
- if (testContext.usesDynamicLoginMethods) {
+ if (true) {
if (testContext.clientRecipeListForDynamicLogin) {
enabledRecipes = JSON.parse(testContext.clientRecipeListForDynamicLogin);
} else {
diff --git a/lib/build/emailverification-shared.js b/lib/build/emailverification-shared.js
index f025cfe3e..d6a0e2799 100644
--- a/lib/build/emailverification-shared.js
+++ b/lib/build/emailverification-shared.js
@@ -18,6 +18,22 @@ var _a = genericComponentOverrideContext.createGenericComponentsOverrideContext(
useContext = _a[0],
Provider = _a[1];
+/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
+ *
+ * This software is licensed under the Apache License, Version 2.0 (the
+ * "License") as published by the Apache Software Foundation.
+ *
+ * You may not use this file except in compliance with the License. You may
+ * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+var DEFAULT_VERIFY_EMAIL_PATH = "/verify-email";
+
var EmailVerificationClaimClass = /** @class */ (function (_super) {
genericComponentOverrideContext.__extends(EmailVerificationClaimClass, _super);
function EmailVerificationClaimClass(getRecipeImpl, onFailureRedirection) {
@@ -57,22 +73,6 @@ var EmailVerificationClaimClass = /** @class */ (function (_super) {
return EmailVerificationClaimClass;
})(EmailVerificationWebJS.EmailVerificationClaimClass);
-/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
- *
- * This software is licensed under the Apache License, Version 2.0 (the
- * "License") as published by the Apache Software Foundation.
- *
- * You may not use this file except in compliance with the License. You may
- * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations
- * under the License.
- */
-var DEFAULT_VERIFY_EMAIL_PATH = "/verify-email";
-
var getFunctionOverrides = function (onHandleEvent) {
return function (originalImp) {
return genericComponentOverrideContext.__assign(genericComponentOverrideContext.__assign({}, originalImp), {
diff --git a/lib/build/index2.js b/lib/build/index2.js
index 75c54626c..0b24c118d 100644
--- a/lib/build/index2.js
+++ b/lib/build/index2.js
@@ -408,8 +408,6 @@ var priorityOrder = [
{ rid: "thirdparty", includes: ["thirdparty"], factorsProvided: ["thirdparty"] },
];
function chooseComponentBasedOnFirstFactors(firstFactors, routeComponents) {
- console.log("chooseComponentBasedOnFirstFactors");
- console.log({ firstFactors: firstFactors });
var _loop_1 = function (rid, factorsProvided) {
if (
firstFactors.length === factorsProvided.length &&
@@ -420,7 +418,6 @@ function chooseComponentBasedOnFirstFactors(firstFactors, routeComponents) {
var matchingComp = routeComponents.find(function (comp) {
return comp.recipeID === rid;
});
- console.log("exact match", rid, !!matchingComp);
if (matchingComp) {
return { value: matchingComp };
}
@@ -440,18 +437,10 @@ function chooseComponentBasedOnFirstFactors(firstFactors, routeComponents) {
var providedByCurrent = factorsProvided.filter(function (id) {
return firstFactors.includes(id);
}).length;
- console.log({
- rid: rid,
- providedByCurrent: factorsProvided.filter(function (id) {
- return firstFactors.includes(id);
- }),
- c: providedByCurrent,
- });
if (providedByCurrent >= maxProvided) {
var matchingComp = routeComponents.find(function (comp) {
return comp.recipeID === rid;
});
- console.log("new max", rid);
if (matchingComp) {
maxProvided = providedByCurrent;
component = matchingComp;
@@ -531,11 +520,6 @@ var RecipeRouter = /** @class */ (function () {
})
.includes(comp.recipeID);
});
- console.log({
- defaultToStaticList: defaultToStaticList,
- matchingNonAuthComponent: !!matchingNonAuthComponent,
- componentMatchingRid: !!componentMatchingRid,
- });
if (matchingNonAuthComponent) {
return matchingNonAuthComponent;
}
@@ -544,15 +528,12 @@ var RecipeRouter = /** @class */ (function () {
}
var mfaRecipe = recipe.MultiFactorAuth.getInstance();
if (genericComponentOverrideContext.SuperTokens.usesDynamicLoginMethods === false) {
- console.log("usesDynamicLoginMethods false");
if (componentMatchingRid) {
return componentMatchingRid;
}
if (mfaRecipe) {
- console.log("mfa enabled");
return chooseComponentBasedOnFirstFactors(mfaRecipe.config.getFirstFactors(), routeComponents);
} else {
- console.log("returning default");
return defaultComp;
}
}
diff --git a/lib/build/multifactorauth-shared.js b/lib/build/multifactorauth-shared.js
index 21a0fff16..4bec6918c 100644
--- a/lib/build/multifactorauth-shared.js
+++ b/lib/build/multifactorauth-shared.js
@@ -39,6 +39,98 @@ var getFunctionOverrides = function (
};
};
+var MultiFactorAuthClaimClass = /** @class */ (function () {
+ function MultiFactorAuthClaimClass(getRecipeImpl, getRedirectURL, onFailureRedirection) {
+ var _this = this;
+ this.webJSClaim = new MultiFactorAuthWebJS.MultiFactorAuthClaimClass(getRecipeImpl);
+ this.refresh = this.webJSClaim.refresh;
+ this.getLastFetchedTime = this.webJSClaim.getLastFetchedTime;
+ this.getValueFromPayload = this.webJSClaim.getValueFromPayload;
+ this.id = this.webJSClaim.id;
+ var defaultOnFailureRedirection = function (_a) {
+ var reason = _a.reason,
+ userContext = _a.userContext;
+ if (reason.nextFactorOptions) {
+ if (reason.nextFactorOptions.length === 1) {
+ return getRedirectURL(
+ { action: "GO_TO_FACTOR", factorId: reason.nextFactorOptions[0] },
+ userContext
+ );
+ } else {
+ return getRedirectURL({ action: "FACTOR_CHOICE_REQUIRED" }, userContext);
+ }
+ }
+ return getRedirectURL({ action: "GO_TO_FACTOR", factorId: reason.factorId }, userContext);
+ };
+ this.validators = genericComponentOverrideContext.__assign(
+ genericComponentOverrideContext.__assign({}, this.webJSClaim.validators),
+ {
+ hasCompletedDefaultFactors: function (doRedirection, showAccessDeniedOnFailure) {
+ if (doRedirection === void 0) {
+ doRedirection = true;
+ }
+ if (showAccessDeniedOnFailure === void 0) {
+ showAccessDeniedOnFailure = true;
+ }
+ var orig = _this.webJSClaim.validators.hasCompletedDefaultFactors();
+ return genericComponentOverrideContext.__assign(
+ genericComponentOverrideContext.__assign({}, orig),
+ {
+ showAccessDeniedOnFailure: showAccessDeniedOnFailure,
+ onFailureRedirection:
+ onFailureRedirection !== null && onFailureRedirection !== void 0
+ ? onFailureRedirection
+ : function (
+ _a // TODO: feels brittle to rely on reason
+ ) {
+ var reason = _a.reason,
+ userContext = _a.userContext;
+ return doRedirection
+ ? defaultOnFailureRedirection({
+ reason: reason,
+ userContext: userContext,
+ })
+ : undefined;
+ },
+ }
+ );
+ },
+ hasCompletedFactors: function (requirements, doRedirection, showAccessDeniedOnFailure) {
+ if (doRedirection === void 0) {
+ doRedirection = true;
+ }
+ if (showAccessDeniedOnFailure === void 0) {
+ showAccessDeniedOnFailure = true;
+ }
+ var orig = _this.webJSClaim.validators.hasCompletedFactors(requirements);
+ return genericComponentOverrideContext.__assign(
+ genericComponentOverrideContext.__assign({}, orig),
+ {
+ showAccessDeniedOnFailure: showAccessDeniedOnFailure,
+ onFailureRedirection:
+ onFailureRedirection !== null && onFailureRedirection !== void 0
+ ? onFailureRedirection
+ : function (
+ _a // TODO: feels brittle to rely on reason
+ ) {
+ var reason = _a.reason,
+ userContext = _a.userContext;
+ return doRedirection
+ ? defaultOnFailureRedirection({
+ reason: reason,
+ userContext: userContext,
+ })
+ : undefined;
+ },
+ }
+ );
+ },
+ }
+ );
+ }
+ return MultiFactorAuthClaimClass;
+})();
+
/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
*
* This software is licensed under the Apache License, Version 2.0 (the
@@ -116,7 +208,7 @@ var MultiFactorAuth = /** @class */ (function (_super) {
_this.getDefaultRedirectionURL = function (context) {
return genericComponentOverrideContext.__awaiter(_this, void 0, void 0, function () {
var chooserPath, redirectInfo;
- return genericComponentOverrideContext.__generator(this, function (_a) {
+ return genericComponentOverrideContext.__generator(this, function (_b) {
if (context.action === "FACTOR_CHOICE_REQUIRED") {
chooserPath = new NormalisedURLPath__default.default(DEFAULT_FACTOR_CHOOSER_PATH);
return [
@@ -199,12 +291,21 @@ var MultiFactorAuth = /** @class */ (function (_super) {
return this.firstFactors;
};
MultiFactorAuth.prototype.addMFAFactors = function (firstFactors, secondaryFactors) {
- var _a, _b;
- (_a = this.firstFactors).push.apply(_a, firstFactors);
- (_b = this.factorRedirectionInfo).push.apply(_b, secondaryFactors);
+ var _b, _c;
+ (_b = this.firstFactors).push.apply(_b, firstFactors);
+ (_c = this.factorRedirectionInfo).push.apply(_c, secondaryFactors);
};
+ var _a;
+ _a = MultiFactorAuth;
MultiFactorAuth.RECIPE_ID = "multifactorauth";
- MultiFactorAuth.MultiFactorAuthClaim = MultiFactorAuthWebJS.MultiFactorAuthClaim;
+ MultiFactorAuth.MultiFactorAuthClaim = new MultiFactorAuthClaimClass(
+ function () {
+ return MultiFactorAuth.getInstanceOrThrow().webJSRecipe;
+ },
+ function (context) {
+ return _a.getInstanceOrThrow().getDefaultRedirectionURL(context);
+ }
+ );
return MultiFactorAuth;
})(index.RecipeModule);
diff --git a/lib/build/passwordless-shared2.js b/lib/build/passwordless-shared2.js
index 06f1fa598..addc013cf 100644
--- a/lib/build/passwordless-shared2.js
+++ b/lib/build/passwordless-shared2.js
@@ -2,7 +2,9 @@
var genericComponentOverrideContext = require("./genericComponentOverrideContext.js");
var PasswordlessWebJS = require("supertokens-web-js/recipe/passwordless");
+var postSuperTokensInitCallbacks = require("supertokens-web-js/utils/postSuperTokensInitCallbacks");
var utils = require("./authRecipe-shared.js");
+var recipe = require("./multifactorauth-shared.js");
var windowHandler = require("supertokens-web-js/utils/windowHandler");
function _interopDefault(e) {
@@ -493,6 +495,22 @@ var Passwordless = /** @class */ (function (_super) {
}
Passwordless.init = function (config) {
var normalisedConfig = normalisePasswordlessConfig(config);
+ postSuperTokensInitCallbacks.PostSuperTokensInitCallbacks.addPostInitCallback(function () {
+ var mfa = recipe.MultiFactorAuth.getInstance();
+ if (mfa !== undefined) {
+ mfa.addMFAFactors(
+ ["otp-phone", "otp-email", "link-phone", "link-email"],
+ [
+ {
+ id: "otp-phone",
+ name: "Phone-based OTP",
+ description: "OTP delivered by a text message",
+ path: "/check-auth/otp",
+ },
+ ]
+ );
+ }
+ });
return {
recipeID: Passwordless.RECIPE_ID,
authReact: function (appInfo) {
diff --git a/lib/build/passwordless.js b/lib/build/passwordless.js
index f8cba3862..ce950e342 100644
--- a/lib/build/passwordless.js
+++ b/lib/build/passwordless.js
@@ -20,6 +20,9 @@ require("./authRecipe-shared.js");
require("./recipeModule-shared.js");
require("./session-shared2.js");
require("supertokens-web-js/recipe/session");
+require("./multifactorauth-shared.js");
+require("supertokens-web-js/recipe/multifactorauth");
+require("supertokens-web-js/utils/sessionClaimValidatorStore");
/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.
*
diff --git a/lib/build/claims/emailVerificationClaim.d.ts b/lib/build/recipe/emailverification/emailVerificationClaim.d.ts
similarity index 86%
rename from lib/build/claims/emailVerificationClaim.d.ts
rename to lib/build/recipe/emailverification/emailVerificationClaim.d.ts
index 1308ba5a1..732815830 100644
--- a/lib/build/claims/emailVerificationClaim.d.ts
+++ b/lib/build/recipe/emailverification/emailVerificationClaim.d.ts
@@ -1,5 +1,5 @@
import { EmailVerificationClaimClass as EmailVerificationClaimClassWebJS } from "supertokens-web-js/recipe/emailverification";
-import type { ValidationFailureCallback } from "../types";
+import type { ValidationFailureCallback } from "../../types";
import type { RecipeInterface } from "supertokens-web-js/recipe/emailverification";
export declare class EmailVerificationClaimClass extends EmailVerificationClaimClassWebJS {
constructor(getRecipeImpl: () => RecipeInterface, onFailureRedirection?: ValidationFailureCallback);
diff --git a/lib/build/recipe/emailverification/index.d.ts b/lib/build/recipe/emailverification/index.d.ts
index 4a0c471e9..f03efd525 100644
--- a/lib/build/recipe/emailverification/index.d.ts
+++ b/lib/build/recipe/emailverification/index.d.ts
@@ -4,7 +4,7 @@ import { GetRedirectionURLContext, PreAPIHookContext, OnHandleEventContext } fro
import { UserInput } from "./types";
import type { RecipeFunctionOptions } from "supertokens-web-js/recipe/emailverification";
export default class Wrapper {
- static EmailVerificationClaim: import("../../claims/emailVerificationClaim").EmailVerificationClaimClass;
+ static EmailVerificationClaim: import("./emailVerificationClaim").EmailVerificationClaimClass;
static init(
config?: UserInput
): import("../../types").RecipeInitResult<
@@ -43,7 +43,7 @@ declare const EmailVerificationComponentsOverrideProvider: import("react").FC<
components: import("./types").ComponentOverrideMap;
}>
>;
-declare const EmailVerificationClaim: import("../../claims/emailVerificationClaim").EmailVerificationClaimClass;
+declare const EmailVerificationClaim: import("./emailVerificationClaim").EmailVerificationClaimClass;
export {
init,
isEmailVerified,
diff --git a/lib/build/recipe/emailverification/recipe.d.ts b/lib/build/recipe/emailverification/recipe.d.ts
index bac2db2ea..5944564d7 100644
--- a/lib/build/recipe/emailverification/recipe.d.ts
+++ b/lib/build/recipe/emailverification/recipe.d.ts
@@ -1,6 +1,6 @@
import EmailVerificationWebJS from "supertokens-web-js/recipe/emailverification";
-import { EmailVerificationClaimClass } from "../../claims/emailVerificationClaim";
import RecipeModule from "../recipeModule";
+import { EmailVerificationClaimClass } from "./emailVerificationClaim";
import type {
UserInput,
NormalisedConfig,
diff --git a/lib/build/recipe/multifactorauth/index.d.ts b/lib/build/recipe/multifactorauth/index.d.ts
index 4794457c1..3b28cd3c9 100644
--- a/lib/build/recipe/multifactorauth/index.d.ts
+++ b/lib/build/recipe/multifactorauth/index.d.ts
@@ -1,11 +1,11 @@
///
-import { RecipeInterface } from "supertokens-web-js/recipe/emailverification";
+import { RecipeInterface } from "supertokens-web-js/recipe/multifactorauth";
import { GetRedirectionURLContext, PreAPIHookContext, OnHandleEventContext } from "./types";
import { UserInput } from "./types";
-import type { MFAFactorInfo } from "supertokens-web-js/lib/build/recipe/multifactorauth/types";
-import type { RecipeFunctionOptions } from "supertokens-web-js/recipe/emailverification";
+import type { RecipeFunctionOptions } from "supertokens-web-js/recipe/multifactorauth";
+import type { MFAFactorInfo } from "supertokens-web-js/recipe/multifactorauth/types";
export default class Wrapper {
- static MultiFactorAuthClaim: import("supertokens-web-js/lib/build/recipe/multifactorauth/multiFactorAuthClaim").MultiFactorAuthClaimClass;
+ static MultiFactorAuthClaim: import("./multiFactorAuthClaim").MultiFactorAuthClaimClass;
static init(
config?: UserInput
): import("../../types").RecipeInitResult<
@@ -32,7 +32,7 @@ declare const MultiFactorAuthComponentsOverrideProvider: import("react").FC<
components: import("./types").ComponentOverrideMap;
}>
>;
-declare const MultiFactorAuthClaim: import("supertokens-web-js/lib/build/recipe/multifactorauth/multiFactorAuthClaim").MultiFactorAuthClaimClass;
+declare const MultiFactorAuthClaim: import("./multiFactorAuthClaim").MultiFactorAuthClaimClass;
export {
init,
getMFAInfo,
diff --git a/lib/build/recipe/multifactorauth/multiFactorAuthClaim.d.ts b/lib/build/recipe/multifactorauth/multiFactorAuthClaim.d.ts
new file mode 100644
index 000000000..7caac34d3
--- /dev/null
+++ b/lib/build/recipe/multifactorauth/multiFactorAuthClaim.d.ts
@@ -0,0 +1,48 @@
+import { MultiFactorAuthClaimClass as MultiFactorAuthClaimClassWebJS } from "supertokens-web-js/recipe/multifactorauth";
+import type { SessionClaimValidator, ValidationFailureCallback } from "../../types";
+import type { RecipeInterface } from "supertokens-web-js/recipe/multifactorauth";
+import type { MFARequirementList } from "supertokens-web-js/recipe/multifactorauth/types";
+export declare class MultiFactorAuthClaimClass {
+ private webJSClaim;
+ readonly id: string;
+ readonly refresh: (userContext: any) => Promise;
+ readonly getLastFetchedTime: (payload: any, _userContext?: any) => number | undefined;
+ readonly getValueFromPayload: (
+ payload: any,
+ _userContext?: any
+ ) =>
+ | {
+ c: Record;
+ n: string[];
+ }
+ | undefined;
+ validators: Omit<
+ MultiFactorAuthClaimClassWebJS["validators"],
+ "hasCompletedDefaultFactors" | "hasCompletedFactors"
+ > & {
+ hasCompletedDefaultFactors: (
+ doRedirection?: boolean,
+ showAccessDeniedOnFailure?: boolean
+ ) => SessionClaimValidator;
+ hasCompletedFactors: (
+ requirements: MFARequirementList,
+ doRedirection?: boolean,
+ showAccessDeniedOnFailure?: boolean
+ ) => SessionClaimValidator;
+ };
+ constructor(
+ getRecipeImpl: () => RecipeInterface,
+ getRedirectURL: (
+ context:
+ | {
+ action: "GO_TO_FACTOR";
+ factorId: string;
+ }
+ | {
+ action: "FACTOR_CHOICE_REQUIRED";
+ },
+ userContext: any
+ ) => Promise,
+ onFailureRedirection?: ValidationFailureCallback
+ );
+}
diff --git a/lib/build/recipe/multifactorauth/recipe.d.ts b/lib/build/recipe/multifactorauth/recipe.d.ts
index 1abf88682..c4aa11fe1 100644
--- a/lib/build/recipe/multifactorauth/recipe.d.ts
+++ b/lib/build/recipe/multifactorauth/recipe.d.ts
@@ -1,5 +1,6 @@
import MultiFactorAuthWebJS from "supertokens-web-js/recipe/multifactorauth";
import RecipeModule from "../recipeModule";
+import { MultiFactorAuthClaimClass } from "./multiFactorAuthClaim";
import type {
UserInput,
NormalisedConfig,
@@ -18,7 +19,7 @@ export default class MultiFactorAuth extends RecipeModule<
readonly webJSRecipe: WebJSRecipeInterface;
static instance?: MultiFactorAuth;
static RECIPE_ID: string;
- static MultiFactorAuthClaim: import("supertokens-web-js/lib/build/recipe/multifactorauth/multiFactorAuthClaim").MultiFactorAuthClaimClass;
+ static MultiFactorAuthClaim: MultiFactorAuthClaimClass;
recipeID: string;
firstFactors: string[];
factorRedirectionInfo: SecondaryFactorRedirectionInfo[];
diff --git a/lib/build/thirdpartypasswordless.js b/lib/build/thirdpartypasswordless.js
index 7de9631c9..796cbe40c 100644
--- a/lib/build/thirdpartypasswordless.js
+++ b/lib/build/thirdpartypasswordless.js
@@ -26,6 +26,9 @@ require("supertokens-web-js/lib/build/normalisedURLPath");
require("supertokens-web-js/recipe/thirdpartypasswordless");
require("./passwordless-shared2.js");
require("supertokens-web-js/recipe/passwordless");
+require("./multifactorauth-shared.js");
+require("supertokens-web-js/recipe/multifactorauth");
+require("supertokens-web-js/utils/sessionClaimValidatorStore");
var Wrapper = /** @class */ (function () {
function Wrapper() {}
diff --git a/lib/ts/claims/emailVerificationClaim.ts b/lib/ts/recipe/emailverification/emailVerificationClaim.ts
similarity index 92%
rename from lib/ts/claims/emailVerificationClaim.ts
rename to lib/ts/recipe/emailverification/emailVerificationClaim.ts
index ec735c115..a1929fb26 100644
--- a/lib/ts/claims/emailVerificationClaim.ts
+++ b/lib/ts/recipe/emailverification/emailVerificationClaim.ts
@@ -1,8 +1,8 @@
import { EmailVerificationClaimClass as EmailVerificationClaimClassWebJS } from "supertokens-web-js/recipe/emailverification";
-import EmailVerification from "../recipe/emailverification/recipe";
+import EmailVerification from "./recipe";
-import type { ValidationFailureCallback } from "../types";
+import type { ValidationFailureCallback } from "../../types";
import type { RecipeInterface } from "supertokens-web-js/recipe/emailverification";
export class EmailVerificationClaimClass extends EmailVerificationClaimClassWebJS {
diff --git a/lib/ts/recipe/emailverification/recipe.tsx b/lib/ts/recipe/emailverification/recipe.tsx
index 8d2acc62e..411f31e78 100644
--- a/lib/ts/recipe/emailverification/recipe.tsx
+++ b/lib/ts/recipe/emailverification/recipe.tsx
@@ -22,11 +22,11 @@ import NormalisedURLPath from "supertokens-web-js/utils/normalisedURLPath";
import { PostSuperTokensInitCallbacks } from "supertokens-web-js/utils/postSuperTokensInitCallbacks";
import { SessionClaimValidatorStore } from "supertokens-web-js/utils/sessionClaimValidatorStore";
-import { EmailVerificationClaimClass } from "../../claims/emailVerificationClaim";
import { SSR_ERROR } from "../../constants";
import RecipeModule from "../recipeModule";
import { DEFAULT_VERIFY_EMAIL_PATH } from "./constants";
+import { EmailVerificationClaimClass } from "./emailVerificationClaim";
import { getFunctionOverrides } from "./functionOverrides";
import { normaliseEmailVerificationFeature } from "./utils";
diff --git a/lib/ts/recipe/multifactorauth/index.ts b/lib/ts/recipe/multifactorauth/index.ts
index 572c17f70..5161e7145 100644
--- a/lib/ts/recipe/multifactorauth/index.ts
+++ b/lib/ts/recipe/multifactorauth/index.ts
@@ -16,7 +16,7 @@
/*
* Imports.
*/
-import { RecipeInterface } from "supertokens-web-js/recipe/emailverification";
+import { RecipeInterface } from "supertokens-web-js/recipe/multifactorauth";
import { getNormalisedUserContext } from "../../utils";
@@ -25,8 +25,8 @@ import MultiFactorAuthRecipe from "./recipe";
import { GetRedirectionURLContext, PreAPIHookContext, OnHandleEventContext } from "./types";
import { UserInput } from "./types";
-import type { MFAFactorInfo } from "supertokens-web-js/lib/build/recipe/multifactorauth/types";
-import type { RecipeFunctionOptions } from "supertokens-web-js/recipe/emailverification";
+import type { RecipeFunctionOptions } from "supertokens-web-js/recipe/multifactorauth";
+import type { MFAFactorInfo } from "supertokens-web-js/recipe/multifactorauth/types";
export default class Wrapper {
static MultiFactorAuthClaim = MultiFactorAuthRecipe.MultiFactorAuthClaim;
diff --git a/lib/ts/recipe/multifactorauth/multiFactorAuthClaim.ts b/lib/ts/recipe/multifactorauth/multiFactorAuthClaim.ts
new file mode 100644
index 000000000..4e9428f10
--- /dev/null
+++ b/lib/ts/recipe/multifactorauth/multiFactorAuthClaim.ts
@@ -0,0 +1,92 @@
+import { MultiFactorAuthClaimClass as MultiFactorAuthClaimClassWebJS } from "supertokens-web-js/recipe/multifactorauth";
+
+import type { SessionClaimValidator, ValidationFailureCallback } from "../../types";
+import type { RecipeInterface } from "supertokens-web-js/recipe/multifactorauth";
+import type { MFARequirementList } from "supertokens-web-js/recipe/multifactorauth/types";
+
+export class MultiFactorAuthClaimClass {
+ private webJSClaim: MultiFactorAuthClaimClassWebJS;
+ public readonly id: string;
+ public readonly refresh: (userContext: any) => Promise;
+ public readonly getLastFetchedTime: (payload: any, _userContext?: any) => number | undefined;
+ public readonly getValueFromPayload: (
+ payload: any,
+ _userContext?: any
+ ) => { c: Record; n: string[] } | undefined;
+ public validators: Omit<
+ MultiFactorAuthClaimClassWebJS["validators"],
+ "hasCompletedDefaultFactors" | "hasCompletedFactors"
+ > & {
+ hasCompletedDefaultFactors: (
+ doRedirection?: boolean,
+ showAccessDeniedOnFailure?: boolean
+ ) => SessionClaimValidator;
+ hasCompletedFactors: (
+ requirements: MFARequirementList,
+ doRedirection?: boolean,
+ showAccessDeniedOnFailure?: boolean
+ ) => SessionClaimValidator;
+ };
+
+ constructor(
+ getRecipeImpl: () => RecipeInterface,
+ getRedirectURL: (
+ context: { action: "GO_TO_FACTOR"; factorId: string } | { action: "FACTOR_CHOICE_REQUIRED" },
+ userContext: any
+ ) => Promise,
+ onFailureRedirection?: ValidationFailureCallback
+ ) {
+ this.webJSClaim = new MultiFactorAuthClaimClassWebJS(getRecipeImpl);
+ this.refresh = this.webJSClaim.refresh;
+ this.getLastFetchedTime = this.webJSClaim.getLastFetchedTime;
+ this.getValueFromPayload = this.webJSClaim.getValueFromPayload;
+ this.id = this.webJSClaim.id;
+
+ const defaultOnFailureRedirection = ({ reason, userContext }: any) => {
+ if (reason.nextFactorOptions) {
+ if (reason.nextFactorOptions.length === 1) {
+ return getRedirectURL(
+ { action: "GO_TO_FACTOR", factorId: reason.nextFactorOptions[0] },
+ userContext
+ );
+ } else {
+ return getRedirectURL({ action: "FACTOR_CHOICE_REQUIRED" }, userContext);
+ }
+ }
+ return getRedirectURL({ action: "GO_TO_FACTOR", factorId: reason.factorId }, userContext);
+ };
+
+ this.validators = {
+ ...this.webJSClaim.validators,
+ hasCompletedDefaultFactors: (doRedirection = true, showAccessDeniedOnFailure = true) => {
+ const orig = this.webJSClaim.validators.hasCompletedDefaultFactors();
+ return {
+ ...orig,
+ showAccessDeniedOnFailure,
+ onFailureRedirection:
+ onFailureRedirection ??
+ ((
+ { reason, userContext } // TODO: feels brittle to rely on reason
+ ) => (doRedirection ? defaultOnFailureRedirection({ reason, userContext }) : undefined)),
+ };
+ },
+
+ hasCompletedFactors: (
+ requirements: MFARequirementList,
+ doRedirection = true,
+ showAccessDeniedOnFailure = true
+ ) => {
+ const orig = this.webJSClaim.validators.hasCompletedFactors(requirements);
+ return {
+ ...orig,
+ showAccessDeniedOnFailure,
+ onFailureRedirection:
+ onFailureRedirection ??
+ ((
+ { reason, userContext } // TODO: feels brittle to rely on reason
+ ) => (doRedirection ? defaultOnFailureRedirection({ reason, userContext }) : undefined)),
+ };
+ },
+ };
+ }
+}
diff --git a/lib/ts/recipe/multifactorauth/recipe.tsx b/lib/ts/recipe/multifactorauth/recipe.tsx
index e6a71bb36..051385c43 100644
--- a/lib/ts/recipe/multifactorauth/recipe.tsx
+++ b/lib/ts/recipe/multifactorauth/recipe.tsx
@@ -18,7 +18,6 @@
*/
import MultiFactorAuthWebJS from "supertokens-web-js/recipe/multifactorauth";
-import { MultiFactorAuthClaim } from "supertokens-web-js/recipe/multifactorauth";
import NormalisedURLPath from "supertokens-web-js/utils/normalisedURLPath";
import { PostSuperTokensInitCallbacks } from "supertokens-web-js/utils/postSuperTokensInitCallbacks";
import { SessionClaimValidatorStore } from "supertokens-web-js/utils/sessionClaimValidatorStore";
@@ -28,6 +27,7 @@ import RecipeModule from "../recipeModule";
import { DEFAULT_FACTOR_CHOOSER_PATH } from "./constants";
import { getFunctionOverrides } from "./functionOverrides";
+import { MultiFactorAuthClaimClass } from "./multiFactorAuthClaim";
import { normaliseMultiFactorAuthFeature } from "./utils";
import type {
@@ -50,7 +50,10 @@ export default class MultiFactorAuth extends RecipeModule<
static instance?: MultiFactorAuth;
static RECIPE_ID = "multifactorauth";
- static MultiFactorAuthClaim = MultiFactorAuthClaim;
+ static MultiFactorAuthClaim = new MultiFactorAuthClaimClass(
+ () => MultiFactorAuth.getInstanceOrThrow().webJSRecipe,
+ (context) => this.getInstanceOrThrow().getDefaultRedirectionURL(context)
+ );
public recipeID = MultiFactorAuth.RECIPE_ID;
public firstFactors: string[] = [];
diff --git a/lib/ts/recipe/passwordless/recipe.tsx b/lib/ts/recipe/passwordless/recipe.tsx
index 6a2fe6ac6..e0075cf5b 100644
--- a/lib/ts/recipe/passwordless/recipe.tsx
+++ b/lib/ts/recipe/passwordless/recipe.tsx
@@ -18,10 +18,12 @@
*/
import PasswordlessWebJS from "supertokens-web-js/recipe/passwordless";
+import { PostSuperTokensInitCallbacks } from "supertokens-web-js/utils/postSuperTokensInitCallbacks";
import { SSR_ERROR } from "../../constants";
import { isTest } from "../../utils";
import AuthRecipe from "../authRecipe";
+import MultiFactorAuth from "../multifactorauth/recipe";
import { getFunctionOverrides } from "./functionOverrides";
import { normalisePasswordlessConfig } from "./utils";
@@ -67,6 +69,23 @@ export default class Passwordless extends AuthRecipe<
): RecipeInitResult {
const normalisedConfig = normalisePasswordlessConfig(config);
+ PostSuperTokensInitCallbacks.addPostInitCallback(() => {
+ const mfa = MultiFactorAuth.getInstance();
+ if (mfa !== undefined) {
+ mfa.addMFAFactors(
+ ["otp-phone", "otp-email", "link-phone", "link-email"],
+ [
+ {
+ id: "otp-phone",
+ name: "Phone-based OTP",
+ description: "OTP delivered by a text message",
+ path: "/check-auth/otp",
+ },
+ ]
+ );
+ }
+ });
+
return {
recipeID: Passwordless.RECIPE_ID,
authReact: (
diff --git a/test/server/index.js b/test/server/index.js
index f001d7fdb..c112eca70 100644
--- a/test/server/index.js
+++ b/test/server/index.js
@@ -382,6 +382,18 @@ app.post(
}
);
+app.post("/payload", verifySession(), async (req, res) => {
+ let session = req.session;
+
+ await session.mergeIntoAccessTokenPayload(req.body);
+
+ res.send({ status: "OK" });
+});
+
+app.get("/auth/mfa/info", (req, res) => {
+ res.send({ status: "OK" });
+});
+
app.get("/token", async (_, res) => {
res.send({
latestURLWithToken,