From a9ce3b5c097fba9062659d06b011a1deaa9709bd Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Wed, 1 Nov 2023 00:16:46 +0100 Subject: [PATCH 1/2] feat: self-review fixes & updates --- examples/for-tests/src/App.js | 6 +- examples/for-tests/src/testContext.js | 1 + lib/build/authRecipe-shared2.js | 2 +- lib/build/components/assets/linkIcon.d.ts | 2 - lib/build/emailpasswordprebuiltui.js | 2 +- lib/build/emailverificationprebuiltui.js | 4 +- lib/build/index2.js | 22 +- lib/build/multifactorauth-shared.js | 106 ++++-- lib/build/multifactorauth-shared2.js | 16 + lib/build/multifactorauth.js | 2 + lib/build/multifactorauthprebuiltui.js | 33 +- lib/build/passwordless-shared2.js | 42 +-- lib/build/passwordless-shared3.js | 2 +- lib/build/passwordlessprebuiltui.js | 2 +- .../components/themes/translations.d.ts | 2 + lib/build/recipe/multifactorauth/index.d.ts | 6 + lib/build/recipe/multifactorauth/recipe.d.ts | 7 +- lib/build/recipe/multifactorauth/types.d.ts | 5 +- .../features/accessDeniedScreen/index.d.ts | 2 + lib/build/recipe/session/prebuiltui.d.ts | 8 +- lib/build/recipe/session/types.d.ts | 3 + lib/build/session-shared.js | 304 +++++++++++++++++- lib/build/session-shared3.js | 304 +----------------- lib/build/session.js | 4 +- lib/build/sessionprebuiltui.js | 25 +- .../thirdpartyemailpasswordprebuiltui.js | 2 +- lib/build/thirdpartypasswordlessprebuiltui.js | 2 +- lib/build/thirdpartyprebuiltui.js | 2 +- lib/build/ui-entry.js | 2 +- lib/ts/components/assets/linkIcon.tsx | 12 - .../features/factorChooser/index.tsx | 13 +- .../components/themes/factorChooser/index.tsx | 13 +- .../components/themes/translations.ts | 4 + lib/ts/recipe/multifactorauth/index.ts | 12 + lib/ts/recipe/multifactorauth/recipe.tsx | 69 ++-- lib/ts/recipe/multifactorauth/types.ts | 5 +- lib/ts/recipe/passwordless/recipe.tsx | 21 +- lib/ts/recipe/recipeRouter/index.tsx | 9 +- .../features/accessDeniedScreen/index.tsx | 13 +- .../themes/accessDeniedScreenTheme/index.tsx | 1 + .../session/components/themes/styles.css | 1 + lib/ts/recipe/session/prebuiltui.tsx | 11 +- lib/ts/recipe/session/types.ts | 3 + 43 files changed, 629 insertions(+), 478 deletions(-) delete mode 100644 lib/build/components/assets/linkIcon.d.ts delete mode 100644 lib/ts/components/assets/linkIcon.tsx diff --git a/examples/for-tests/src/App.js b/examples/for-tests/src/App.js index 6273474b2..3efe4f4e5 100644 --- a/examples/for-tests/src/App.js +++ b/examples/for-tests/src/App.js @@ -173,7 +173,7 @@ const testContext = getTestContext(); let recipeList = [ MultiFactorAuth.init({ - firstFactors: ["emailpassword", "thirdparty"], + firstFactors: testContext.firstFactors, }), Multitenancy.init({ override: { @@ -484,6 +484,10 @@ export function DashboardHelper({ redirectOnLogout, ...props } = {}) {
session context userID: {sessionContext.userId}
{JSON.stringify(sessionContext.invalidClaims, undefined, 2)}
+ MultiFactorAuth.redirectToFactorChooser(true, props.history)}>MFA chooser + MultiFactorAuth.redirectToFactor("totp", true, props.history)}>TOTP + MultiFactorAuth.redirectToFactor("otp-email", true, props.history)}>OTP-Email + MultiFactorAuth.redirectToFactor("otp-phone", true, props.history)}>OTP-Phone ); } diff --git a/examples/for-tests/src/testContext.js b/examples/for-tests/src/testContext.js index ab1e6e3c5..21c43c304 100644 --- a/examples/for-tests/src/testContext.js +++ b/examples/for-tests/src/testContext.js @@ -14,6 +14,7 @@ export function getTestContext() { staticProviderList: localStorage.getItem("staticProviderList"), mockTenantId: localStorage.getItem("mockTenantId"), clientType: localStorage.getItem("clientType") || undefined, + firstFactors: localStorage.getItem("firstFactors")?.split(", "), }; return ret; } diff --git a/lib/build/authRecipe-shared2.js b/lib/build/authRecipe-shared2.js index e03259ffa..25c539965 100644 --- a/lib/build/authRecipe-shared2.js +++ b/lib/build/authRecipe-shared2.js @@ -4,7 +4,7 @@ var genericComponentOverrideContext = require("./genericComponentOverrideContext var jsxRuntime = require("react/jsx-runtime"); var React = require("react"); var uiEntry = require("./index2.js"); -var session = require("./session-shared3.js"); +var session = require("./session-shared.js"); var recipe = require("./session-shared2.js"); /** diff --git a/lib/build/components/assets/linkIcon.d.ts b/lib/build/components/assets/linkIcon.d.ts deleted file mode 100644 index 35d45fe65..000000000 --- a/lib/build/components/assets/linkIcon.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -/// -export declare const LinkIcon: () => JSX.Element; diff --git a/lib/build/emailpasswordprebuiltui.js b/lib/build/emailpasswordprebuiltui.js index d973d6318..58e98cf2d 100644 --- a/lib/build/emailpasswordprebuiltui.js +++ b/lib/build/emailpasswordprebuiltui.js @@ -26,8 +26,8 @@ require("supertokens-web-js/utils/sessionClaimValidatorStore"); require("./recipeModule-shared.js"); require("./session-shared2.js"); require("supertokens-web-js/recipe/session"); -require("./session-shared3.js"); require("./session-shared.js"); +require("./session-shared3.js"); require("./translations.js"); require("./emailverification-shared2.js"); require("./emailpassword-shared7.js"); diff --git a/lib/build/emailverificationprebuiltui.js b/lib/build/emailverificationprebuiltui.js index c35b9f47a..c5be31f23 100644 --- a/lib/build/emailverificationprebuiltui.js +++ b/lib/build/emailverificationprebuiltui.js @@ -4,7 +4,7 @@ var genericComponentOverrideContext = require("./genericComponentOverrideContext var jsxRuntime = require("react/jsx-runtime"); var NormalisedURLPath = require("supertokens-web-js/utils/normalisedURLPath"); var uiEntry = require("./index2.js"); -var session = require("./session-shared3.js"); +var session = require("./session-shared.js"); var recipe$1 = require("./emailverification-shared.js"); var React = require("react"); var recipe = require("./session-shared2.js"); @@ -29,7 +29,7 @@ require("supertokens-web-js/recipe/multifactorauth"); require("supertokens-web-js/utils/sessionClaimValidatorStore"); require("./recipeModule-shared.js"); require("supertokens-web-js/recipe/session"); -require("./session-shared.js"); +require("./session-shared3.js"); require("supertokens-web-js/recipe/emailverification"); function _interopDefault(e) { diff --git a/lib/build/index2.js b/lib/build/index2.js index 44251fa1e..0bcc45e78 100644 --- a/lib/build/index2.js +++ b/lib/build/index2.js @@ -388,6 +388,7 @@ var DynamicLoginMethodsSpinner = function () { }; // The related ADR: https://supertokens.com/docs/contribute/decisions/multitenancy/0006 +// TODO: This could be by the recipes registering what factors they provide (at least partially) var priorityOrder = [ { rid: "thirdpartyemailpassword", @@ -437,7 +438,7 @@ function chooseComponentBasedOnFirstFactors(firstFactors, routeComponents) { var providedByCurrent = factorsProvided.filter(function (id) { return firstFactors.includes(id); }).length; - if (providedByCurrent >= maxProvided) { + if (providedByCurrent > maxProvided) { var matchingComp = routeComponents.find(function (comp) { return comp.recipeID === rid; }); @@ -448,12 +449,15 @@ function chooseComponentBasedOnFirstFactors(firstFactors, routeComponents) { } }; // We find the component that provides the most factors - for (var _b = 0, _c = priorityOrder.reverse(); _b < _c.length; _b++) { - var _d = _c[_b], - rid = _d.rid, - factorsProvided = _d.factorsProvided; + for (var _b = 0, priorityOrder_2 = priorityOrder; _b < priorityOrder_2.length; _b++) { + var _c = priorityOrder_2[_b], + rid = _c.rid, + factorsProvided = _c.factorsProvided; _loop_2(rid, factorsProvided); } + if (component === undefined) { + throw new Error("No enabled recipes overlap with the requested firstFactors: " + firstFactors); + } return component; } var RecipeRouter = /** @class */ (function () { @@ -578,8 +582,8 @@ var RecipeRouter = /** @class */ (function () { } }; // We first try to find an exact match - for (var _i = 0, priorityOrder_2 = priorityOrder; _i < priorityOrder_2.length; _i++) { - var _b = priorityOrder_2[_i], + for (var _i = 0, priorityOrder_3 = priorityOrder; _i < priorityOrder_3.length; _i++) { + var _b = priorityOrder_3[_i], rid = _b.rid, includes = _b.includes; var state_2 = _loop_3(rid, includes); @@ -600,8 +604,8 @@ var RecipeRouter = /** @class */ (function () { } }; // We try to find a partial match - for (var _c = 0, priorityOrder_3 = priorityOrder; _c < priorityOrder_3.length; _c++) { - var _d = priorityOrder_3[_c], + for (var _c = 0, priorityOrder_4 = priorityOrder; _c < priorityOrder_4.length; _c++) { + var _d = priorityOrder_4[_c], rid = _d.rid, includes = _d.includes; var state_3 = _loop_4(rid, includes); diff --git a/lib/build/multifactorauth-shared.js b/lib/build/multifactorauth-shared.js index 825426998..4058003e0 100644 --- a/lib/build/multifactorauth-shared.js +++ b/lib/build/multifactorauth-shared.js @@ -2,6 +2,7 @@ var genericComponentOverrideContext = require("./genericComponentOverrideContext.js"); var MultiFactorAuthWebJS = require("supertokens-web-js/recipe/multifactorauth"); +var utils = require("supertokens-web-js/utils"); var NormalisedURLPath = require("supertokens-web-js/utils/normalisedURLPath"); var postSuperTokensInitCallbacks = require("supertokens-web-js/utils/postSuperTokensInitCallbacks"); var sessionClaimValidatorStore = require("supertokens-web-js/utils/sessionClaimValidatorStore"); @@ -199,7 +200,7 @@ var MultiFactorAuth = /** @class */ (function (_super) { var _this = _super.call(this, config) || this; _this.webJSRecipe = webJSRecipe; _this.recipeID = MultiFactorAuth.RECIPE_ID; - _this.firstFactors = []; + _this.firstFactors = new Set(); _this.secondaryFactors = []; _this.getDefaultRedirectionURL = function (context) { return genericComponentOverrideContext.__awaiter(_this, void 0, void 0, function () { @@ -209,19 +210,21 @@ var MultiFactorAuth = /** @class */ (function (_super) { chooserPath = new NormalisedURLPath__default.default(DEFAULT_FACTOR_CHOOSER_PATH); return [ 2 /*return*/, - "".concat( - this.config.appInfo.websiteBasePath.appendPath(chooserPath).getAsStringDangerous() - ), + this.config.appInfo.websiteBasePath.appendPath(chooserPath).getAsStringDangerous(), ]; } else if (context.action === "GO_TO_FACTOR") { redirectInfo = this.getSecondaryFactors().find(function (f) { return f.id === context.factorId; }); if (redirectInfo !== undefined) { - return [2 /*return*/, redirectInfo.path]; + return [ + 2 /*return*/, + this.config.appInfo.websiteBasePath + .appendPath(redirectInfo.path) + .getAsStringDangerous(), + ]; } - // TODO: access denied screen if not defined? - return [2 /*return*/, "/"]; + throw new Error("Requested redirect to unknown factor id"); } else { return [2 /*return*/, "/"]; } @@ -280,45 +283,96 @@ var MultiFactorAuth = /** @class */ (function (_super) { } return MultiFactorAuth.instance; }; - MultiFactorAuth.prototype.getDefaultFirstFactors = function () { - return this.firstFactors; - }; MultiFactorAuth.prototype.addMFAFactors = function (firstFactors, secondaryFactors) { - var _b, _c; - (_b = this.firstFactors).push.apply(_b, firstFactors); - (_c = this.secondaryFactors).push.apply(_c, secondaryFactors); + for (var _i = 0, firstFactors_1 = firstFactors; _i < firstFactors_1.length; _i++) { + var fact = firstFactors_1[_i]; + this.firstFactors.add(fact); + } + this.secondaryFactors = genericComponentOverrideContext.__spreadArray( + genericComponentOverrideContext.__spreadArray( + [], + this.secondaryFactors.filter(function (factor) { + return secondaryFactors.every(function (newFactor) { + return factor.id !== newFactor.id; + }); + }), + true + ), + secondaryFactors, + true + ); }; MultiFactorAuth.prototype.getFirstFactors = function () { var _b; - return (_b = this.config.firstFactors) !== null && _b !== void 0 ? _b : this.firstFactors; + return (_b = this.config.firstFactors) !== null && _b !== void 0 ? _b : Array.from(this.firstFactors); }; MultiFactorAuth.prototype.getSecondaryFactors = function () { return this.config.getFactorInfo(this.secondaryFactors); }; - MultiFactorAuth.prototype.redirectToFactor = function (factorId, history) { + MultiFactorAuth.prototype.redirectToFactor = function (factorId, redirectBack, history) { + if (redirectBack === void 0) { + redirectBack = false; + } return genericComponentOverrideContext.__awaiter(this, void 0, void 0, function () { - var _b, _c; - return genericComponentOverrideContext.__generator(this, function (_d) { - switch (_d.label) { + var url, redirectUrl, redirectUrl; + return genericComponentOverrideContext.__generator(this, function (_b) { + switch (_b.label) { case 0: - _c = (_b = genericComponentOverrideContext.SuperTokens.getInstanceOrThrow()).redirectToUrl; return [4 /*yield*/, this.getRedirectUrl({ action: "GO_TO_FACTOR", factorId: factorId })]; case 1: - return [2 /*return*/, _c.apply(_b, [_d.sent(), history])]; + url = _b.sent(); + if (redirectBack) { + redirectUrl = genericComponentOverrideContext + .getCurrentNormalisedUrlPath() + .getAsStringDangerous(); + url = utils.appendQueryParamsToURL(url, { redirectToPath: redirectUrl }); + } else { + redirectUrl = genericComponentOverrideContext.getRedirectToPathFromURL(); + if (redirectUrl) { + url = utils.appendQueryParamsToURL(url, { redirectToPath: redirectUrl }); + } + } + return [ + 2 /*return*/, + genericComponentOverrideContext.SuperTokens.getInstanceOrThrow().redirectToUrl( + url, + history + ), + ]; } }); }); }; - MultiFactorAuth.prototype.redirectToFactorChooser = function (history) { + MultiFactorAuth.prototype.redirectToFactorChooser = function (redirectBack, history) { + if (redirectBack === void 0) { + redirectBack = false; + } return genericComponentOverrideContext.__awaiter(this, void 0, void 0, function () { - var _b, _c; - return genericComponentOverrideContext.__generator(this, function (_d) { - switch (_d.label) { + var url, redirectUrl, redirectUrl; + return genericComponentOverrideContext.__generator(this, function (_b) { + switch (_b.label) { case 0: - _c = (_b = genericComponentOverrideContext.SuperTokens.getInstanceOrThrow()).redirectToUrl; return [4 /*yield*/, this.getRedirectUrl({ action: "FACTOR_CHOOSER" })]; case 1: - return [2 /*return*/, _c.apply(_b, [_d.sent(), history])]; + url = _b.sent(); + if (redirectBack) { + redirectUrl = genericComponentOverrideContext + .getCurrentNormalisedUrlPath() + .getAsStringDangerous(); + url = utils.appendQueryParamsToURL(url, { redirectToPath: redirectUrl }); + } else { + redirectUrl = genericComponentOverrideContext.getRedirectToPathFromURL(); + if (redirectUrl) { + url = utils.appendQueryParamsToURL(url, { redirectToPath: redirectUrl }); + } + } + return [ + 2 /*return*/, + genericComponentOverrideContext.SuperTokens.getInstanceOrThrow().redirectToUrl( + url, + history + ), + ]; } }); }); diff --git a/lib/build/multifactorauth-shared2.js b/lib/build/multifactorauth-shared2.js index 1f8eafe1c..26dfa980a 100644 --- a/lib/build/multifactorauth-shared2.js +++ b/lib/build/multifactorauth-shared2.js @@ -35,12 +35,26 @@ var Wrapper = /** @class */ (function () { }) ); }; + Wrapper.redirectToFactor = function (factorId, redirectBack, history) { + if (redirectBack === void 0) { + redirectBack = true; + } + return recipe.MultiFactorAuth.getInstanceOrThrow().redirectToFactor(factorId, redirectBack, history); + }; + Wrapper.redirectToFactorChooser = function (redirectBack, history) { + if (redirectBack === void 0) { + redirectBack = true; + } + return recipe.MultiFactorAuth.getInstanceOrThrow().redirectToFactorChooser(redirectBack, history); + }; Wrapper.MultiFactorAuthClaim = recipe.MultiFactorAuth.MultiFactorAuthClaim; Wrapper.ComponentsOverrideProvider = Provider; return Wrapper; })(); var init = Wrapper.init; var getMFAInfo = Wrapper.getMFAInfo; +var redirectToFactor = Wrapper.redirectToFactor; +var redirectToFactorChooser = Wrapper.redirectToFactorChooser; var MultiFactorAuthComponentsOverrideProvider = Wrapper.ComponentsOverrideProvider; var MultiFactorAuthClaim = recipe.MultiFactorAuth.MultiFactorAuthClaim; @@ -49,4 +63,6 @@ exports.MultiFactorAuthComponentsOverrideProvider = MultiFactorAuthComponentsOve exports.Wrapper = Wrapper; exports.getMFAInfo = getMFAInfo; exports.init = init; +exports.redirectToFactor = redirectToFactor; +exports.redirectToFactorChooser = redirectToFactorChooser; exports.useContext = useContext; diff --git a/lib/build/multifactorauth.js b/lib/build/multifactorauth.js index 20275f881..222349531 100644 --- a/lib/build/multifactorauth.js +++ b/lib/build/multifactorauth.js @@ -24,3 +24,5 @@ exports.MultiFactorAuthComponentsOverrideProvider = multifactorauth.MultiFactorA exports.default = multifactorauth.Wrapper; exports.getMFAInfo = multifactorauth.getMFAInfo; exports.init = multifactorauth.init; +exports.redirectToFactor = multifactorauth.redirectToFactor; +exports.redirectToFactorChooser = multifactorauth.redirectToFactorChooser; diff --git a/lib/build/multifactorauthprebuiltui.js b/lib/build/multifactorauthprebuiltui.js index afe5e1bc6..753794946 100644 --- a/lib/build/multifactorauthprebuiltui.js +++ b/lib/build/multifactorauthprebuiltui.js @@ -4,17 +4,18 @@ var genericComponentOverrideContext = require("./genericComponentOverrideContext var jsxRuntime = require("react/jsx-runtime"); var NormalisedURLPath = require("supertokens-web-js/utils/normalisedURLPath"); var uiEntry = require("./index2.js"); -var session = require("./session-shared3.js"); +var session = require("./session-shared.js"); var multifactorauth = require("./multifactorauth-shared2.js"); var React = require("react"); +var windowHandler = require("supertokens-web-js/utils/windowHandler"); var recipe$1 = require("./session-shared2.js"); var recipe = require("./multifactorauth-shared.js"); var SuperTokensBranding = require("./SuperTokensBranding.js"); var translations = require("./translations.js"); -var arrowLeftIcon = require("./arrowLeftIcon.js"); var translationContext = require("./translationContext.js"); +var sessionprebuiltui = require("./sessionprebuiltui.js"); +var arrowLeftIcon = require("./arrowLeftIcon.js"); var backButton = require("./emailpassword-shared7.js"); -var windowHandler = require("supertokens-web-js/utils/windowHandler"); require("supertokens-web-js"); require("supertokens-web-js/utils/cookieHandler"); require("supertokens-web-js/utils/postSuperTokensInitCallbacks"); @@ -24,7 +25,7 @@ require("supertokens-web-js/utils/normalisedURLDomain"); require("react-dom"); require("./multitenancy-shared.js"); require("supertokens-web-js/recipe/session"); -require("./session-shared.js"); +require("./session-shared3.js"); require("./recipeModule-shared.js"); require("supertokens-web-js/recipe/multifactorauth"); require("supertokens-web-js/utils/sessionClaimValidatorStore"); @@ -224,6 +225,13 @@ var FactorList = uiEntry.withOverride("MultiFactorAuthFactorList", function Mult }); function FactorChooserTheme(props) { + var t = translationContext.useTranslation(); + if (props.availableFactors.length === 0) { + return jsxRuntime.jsx(sessionprebuiltui.AccessDeniedScreen, { + useShadowDom: props.config.useShadowDom, + error: props.showBackButton ? t("MFA_NO_AVAILABLE_OPTIONS") : t("MFA_NO_AVAILABLE_OPTIONS_LOGIN"), + }); + } return jsxRuntime.jsxs( "div", genericComponentOverrideContext.__assign( @@ -239,7 +247,7 @@ function FactorChooserTheme(props) { mfaInfo: props.mfaInfo, navigateToFactor: props.navigateToFactor, }), - jsxRuntime.jsx(FactorChooserFooter, { logout: props.logout }), + jsxRuntime.jsx(FactorChooserFooter, { logout: props.onLogoutClicked }), jsxRuntime.jsx(SuperTokensBranding.SuperTokensBranding, {}), ], } @@ -276,7 +284,13 @@ function FactorChooserThemeWrapper(props) { var defaultTranslationsMultiFactorAuth = { en: genericComponentOverrideContext.__assign( genericComponentOverrideContext.__assign({}, translations.defaultTranslationsCommon.en), - { MULTI_FACTOR_CHOOSER_HEADER_TITLE: "Please select a second factor", MULTI_FACTOR_AUTH_LOGOUT: "Logout" } + { + MULTI_FACTOR_CHOOSER_HEADER_TITLE: "Please select a second factor", + MULTI_FACTOR_AUTH_LOGOUT: "Logout", + MFA_NO_AVAILABLE_OPTIONS: "You have no available secondary factors.", + MFA_NO_AVAILABLE_OPTIONS_LOGIN: + "You have no available secondary factors and cannot complete the sign-in process. Please contact support.", + } ), }; @@ -339,11 +353,14 @@ var FactorChooser$1 = function (props) { var id = _a.id; return mfaClaimValue.value.n.length === 0 || mfaClaimValue.value.n.includes(id); }); - if (availableFactors.length === 1) { + // If we got here when the next array is not empty, that means that the user redirected here intentionally + // In this case we do not want to automatically redirect away but show the chooser screen. + if (mfaClaimValue.value.n.length !== 0 && availableFactors.length === 1) { return [ 2 /*return*/, recipe.MultiFactorAuth.getInstanceOrThrow().redirectToFactor( availableFactors[0].id, + false, props.history ), ]; @@ -452,7 +469,7 @@ var FactorChooser$1 = function (props) { showBackButton: ((_a = mfaClaimValue.value) === null || _a === void 0 ? void 0 : _a.n.length) === 0, mfaInfo: mfaInfo, availableFactors: availableFactors, - logout: signOut, + onLogoutClicked: signOut, navigateToFactor: navigateToFactor, }; return jsxRuntime.jsx( diff --git a/lib/build/passwordless-shared2.js b/lib/build/passwordless-shared2.js index f352693b5..3c030b493 100644 --- a/lib/build/passwordless-shared2.js +++ b/lib/build/passwordless-shared2.js @@ -2,6 +2,7 @@ var genericComponentOverrideContext = require("./genericComponentOverrideContext.js"); var PasswordlessWebJS = require("supertokens-web-js/recipe/passwordless"); +var NormalisedURLPath = require("supertokens-web-js/utils/normalisedURLPath"); var postSuperTokensInitCallbacks = require("supertokens-web-js/utils/postSuperTokensInitCallbacks"); var jsxRuntime = require("react/jsx-runtime"); var utils = require("./authRecipe-shared.js"); @@ -13,27 +14,7 @@ function _interopDefault(e) { } var PasswordlessWebJS__default = /*#__PURE__*/ _interopDefault(PasswordlessWebJS); - -var LinkIcon = function () { - return jsxRuntime.jsxs( - "svg", - genericComponentOverrideContext.__assign( - { xmlns: "http://www.w3.org/2000/svg", width: "19", height: "16", viewBox: "0 0 19 16", fill: "none" }, - { - children: [ - jsxRuntime.jsx("path", { - d: "M10.9871 11.7876C10.2967 11.7898 9.61895 11.6022 9.0279 11.2453C8.43684 10.8885 7.95517 10.3762 7.63545 9.76427C7.31574 9.15235 7.17027 8.46436 7.21492 7.7754C7.25958 7.08644 7.49264 6.42298 7.88867 5.85745C7.99951 5.70075 8.16806 5.59449 8.35725 5.56206C8.54643 5.52963 8.74075 5.57368 8.89745 5.68452C9.05416 5.79536 9.16042 5.96391 9.19285 6.15309C9.22528 6.34228 9.18123 6.53659 9.07039 6.6933C8.75488 7.13913 8.60843 7.68273 8.65728 8.22672C8.70614 8.77071 8.9471 9.27952 9.337 9.662C9.55327 9.87943 9.81039 10.052 10.0936 10.1697C10.3767 10.2874 10.6804 10.348 10.9871 10.348C11.2938 10.348 11.5974 10.2874 11.8806 10.1697C12.1638 10.052 12.4209 9.87943 12.6372 9.662L16.9605 5.33865C17.3767 4.89673 17.6045 4.31005 17.5954 3.70303C17.5863 3.09602 17.3411 2.51642 16.9118 2.08715C16.4826 1.65788 15.903 1.4127 15.296 1.40362C14.6889 1.39454 14.1023 1.62226 13.6603 2.03849L11.8229 3.87592C11.7557 3.9431 11.676 3.99639 11.5882 4.03275C11.5004 4.06911 11.4063 4.08782 11.3113 4.08782C11.2163 4.08782 11.1222 4.06911 11.0345 4.03275C10.9467 3.99639 10.8669 3.9431 10.7997 3.87592C10.7325 3.80873 10.6793 3.72897 10.6429 3.64119C10.6065 3.55341 10.5878 3.45933 10.5878 3.36432C10.5878 3.26931 10.6065 3.17522 10.6429 3.08744C10.6793 2.99966 10.7325 2.91991 10.7997 2.85272L12.6372 1.0153C13.3543 0.347081 14.3028 -0.0167022 15.2828 0.000589365C16.2628 0.017881 17.1979 0.414897 17.891 1.108C18.5841 1.80109 18.9811 2.73616 18.9984 3.7162C19.0157 4.69623 18.6519 5.64472 17.9837 6.36184L13.6603 10.6852C13.309 11.0356 12.8921 11.3133 12.4334 11.5025C11.9747 11.6916 11.4832 11.7885 10.9871 11.7876Z", - fill: "#1485FF", - }), - jsxRuntime.jsx("path", { - d: "M3.7826 15.3866C3.03483 15.3879 2.3035 15.1672 1.68132 14.7524C1.05914 14.3376 0.574138 13.7474 0.287796 13.0567C0.00145422 12.3659 -0.0733288 11.6057 0.0729281 10.8723C0.219185 10.139 0.579895 9.46563 1.10933 8.93756L5.43268 4.61421C5.78326 4.26285 6.20004 3.9845 6.65891 3.79526C7.11777 3.60603 7.6096 3.50966 8.10595 3.51175C9.10694 3.5154 10.0666 3.91118 10.7792 4.61421C11.4078 5.23969 11.7952 6.06714 11.873 6.95048C11.9508 7.83382 11.7139 8.71624 11.2043 9.44195C11.0935 9.59865 10.925 9.70491 10.7358 9.73734C10.5466 9.76977 10.3523 9.72572 10.1956 9.61488C10.0389 9.50404 9.93261 9.33549 9.90018 9.14631C9.86774 8.95712 9.91179 8.7628 10.0226 8.6061C10.3381 8.16027 10.4846 7.61667 10.4357 7.07268C10.3869 6.52869 10.1459 6.01988 9.75603 5.6374C9.32308 5.18824 8.72968 4.92911 8.10595 4.91684C7.7967 4.92053 7.49134 4.9862 7.20792 5.10997C6.92449 5.23373 6.66877 5.41308 6.45587 5.6374L2.13252 9.96075C1.71629 10.4027 1.48857 10.9894 1.49765 11.5964C1.50673 12.2034 1.75191 12.783 2.18118 13.2122C2.61045 13.6415 3.19005 13.8867 3.79706 13.8958C4.40407 13.9049 4.99075 13.6771 5.43268 13.2609L7.2701 11.4235C7.33729 11.3563 7.41704 11.303 7.50482 11.2666C7.5926 11.2303 7.68669 11.2116 7.7817 11.2116C7.87671 11.2116 7.97079 11.2303 8.05857 11.2666C8.14635 11.303 8.22611 11.3563 8.29329 11.4235C8.36048 11.4907 8.41377 11.5704 8.45013 11.6582C8.48649 11.746 8.5052 11.8401 8.5052 11.9351C8.5052 12.0301 8.48649 12.1242 8.45013 12.212C8.41377 12.2997 8.36048 12.3795 8.29329 12.4467L6.45587 14.2841C6.10457 14.6345 5.68765 14.9122 5.22896 15.1014C4.77026 15.2906 4.27877 15.3875 3.7826 15.3866Z", - fill: "#1485FF", - }), - ], - } - ) - ); -}; +var NormalisedURLPath__default = /*#__PURE__*/ _interopDefault(NormalisedURLPath); var OTPIcon = function () { return jsxRuntime.jsxs( @@ -577,31 +558,16 @@ var Passwordless = /** @class */ (function (_super) { id: "otp-phone", name: "SMS based OTP", description: "Get an OTP code on your phone to complete the authentication request", - path: "/check-auth/otp-phone", + path: new NormalisedURLPath__default.default("/check-auth/otp-phone"), logo: OTPIcon, }, - { - id: "link-phone", - name: "SMS based Magic link", - description: "Get a magic link on your phone to complete the authentication request", - path: "/check-auth/link-phone", - logo: LinkIcon, - }, { id: "otp-email", name: "SMS based OTP", description: "Get an OTP code on your email address to complete the authentication request", - path: "/check-auth/otp-email", + path: new NormalisedURLPath__default.default("/check-auth/otp-email"), logo: OTPIcon, }, - { - id: "link-email", - name: "SMS based Magic link", - description: - "Get a magic link on your email address to complete the authentication request", - path: "/check-auth/link-email", - logo: LinkIcon, - }, ] ); } diff --git a/lib/build/passwordless-shared3.js b/lib/build/passwordless-shared3.js index f712fa3a7..9ab228bb6 100644 --- a/lib/build/passwordless-shared3.js +++ b/lib/build/passwordless-shared3.js @@ -12,7 +12,7 @@ var recipe = require("./session-shared2.js"); var translations = require("./translations.js"); var translationContext = require("./translationContext.js"); var button = require("./emailpassword-shared2.js"); -var session = require("./session-shared3.js"); +var session = require("./session-shared.js"); var recipe$1 = require("./passwordless-shared2.js"); var SuperTokensBranding = require("./SuperTokensBranding.js"); var generalError = require("./emailpassword-shared.js"); diff --git a/lib/build/passwordlessprebuiltui.js b/lib/build/passwordlessprebuiltui.js index 2b277bca2..68f6309d0 100644 --- a/lib/build/passwordlessprebuiltui.js +++ b/lib/build/passwordlessprebuiltui.js @@ -25,8 +25,8 @@ require("supertokens-web-js/utils/sessionClaimValidatorStore"); require("./recipeModule-shared.js"); require("./session-shared2.js"); require("supertokens-web-js/recipe/session"); -require("./session-shared3.js"); require("./session-shared.js"); +require("./session-shared3.js"); require("supertokens-web-js/utils/error"); require("./translations.js"); require("./emailpassword-shared2.js"); diff --git a/lib/build/recipe/multifactorauth/components/themes/translations.d.ts b/lib/build/recipe/multifactorauth/components/themes/translations.d.ts index 93a4eab89..0979f7608 100644 --- a/lib/build/recipe/multifactorauth/components/themes/translations.d.ts +++ b/lib/build/recipe/multifactorauth/components/themes/translations.d.ts @@ -2,6 +2,8 @@ export declare const defaultTranslationsMultiFactorAuth: { en: { MULTI_FACTOR_CHOOSER_HEADER_TITLE: string; MULTI_FACTOR_AUTH_LOGOUT: string; + MFA_NO_AVAILABLE_OPTIONS: string; + MFA_NO_AVAILABLE_OPTIONS_LOGIN: string; BRANDING_POWERED_BY_START: string; BRANDING_POWERED_BY_END: string; SOMETHING_WENT_WRONG_ERROR: string; diff --git a/lib/build/recipe/multifactorauth/index.d.ts b/lib/build/recipe/multifactorauth/index.d.ts index 3b28cd3c9..02c7496be 100644 --- a/lib/build/recipe/multifactorauth/index.d.ts +++ b/lib/build/recipe/multifactorauth/index.d.ts @@ -19,6 +19,8 @@ export default class Wrapper { factors: MFAFactorInfo; fetchResponse: Response; }>; + static redirectToFactor(factorId: string, redirectBack?: boolean, history?: any): Promise; + static redirectToFactorChooser(redirectBack?: boolean, history?: any): Promise; static ComponentsOverrideProvider: import("react").FC< import("react").PropsWithChildren<{ components: import("./types").ComponentOverrideMap; @@ -27,6 +29,8 @@ export default class Wrapper { } declare const init: typeof Wrapper.init; declare const getMFAInfo: typeof Wrapper.getMFAInfo; +declare const redirectToFactor: typeof Wrapper.redirectToFactor; +declare const redirectToFactorChooser: typeof Wrapper.redirectToFactorChooser; declare const MultiFactorAuthComponentsOverrideProvider: import("react").FC< import("react").PropsWithChildren<{ components: import("./types").ComponentOverrideMap; @@ -36,6 +40,8 @@ declare const MultiFactorAuthClaim: import("./multiFactorAuthClaim").MultiFactor export { init, getMFAInfo, + redirectToFactor, + redirectToFactorChooser, MultiFactorAuthComponentsOverrideProvider, GetRedirectionURLContext, PreAPIHookContext as PreAPIHookContext, diff --git a/lib/build/recipe/multifactorauth/recipe.d.ts b/lib/build/recipe/multifactorauth/recipe.d.ts index e25e74e95..bc4f9cc8e 100644 --- a/lib/build/recipe/multifactorauth/recipe.d.ts +++ b/lib/build/recipe/multifactorauth/recipe.d.ts @@ -22,7 +22,7 @@ export default class MultiFactorAuth extends RecipeModule< static MultiFactorAuthClaim: MultiFactorAuthClaimClass; recipeID: string; private readonly firstFactors; - private readonly secondaryFactors; + private secondaryFactors; constructor( config: NormalisedConfigWithAppInfoAndRecipeID, webJSRecipe?: WebJSRecipeInterface @@ -33,10 +33,9 @@ export default class MultiFactorAuth extends RecipeModule< static getInstance(): MultiFactorAuth | undefined; static getInstanceOrThrow(): MultiFactorAuth; getDefaultRedirectionURL: (context: GetRedirectionURLContext) => Promise; - getDefaultFirstFactors(): string[]; addMFAFactors(firstFactors: string[], secondaryFactors: SecondaryFactorRedirectionInfo[]): void; getFirstFactors(): string[]; getSecondaryFactors(): SecondaryFactorRedirectionInfo[]; - redirectToFactor(factorId: string, history?: any): Promise; - redirectToFactorChooser(history?: any): Promise; + redirectToFactor(factorId: string, redirectBack?: boolean, history?: any): Promise; + redirectToFactorChooser(redirectBack?: boolean, history?: any): Promise; } diff --git a/lib/build/recipe/multifactorauth/types.d.ts b/lib/build/recipe/multifactorauth/types.d.ts index 9f9b9b027..93d7086d8 100644 --- a/lib/build/recipe/multifactorauth/types.d.ts +++ b/lib/build/recipe/multifactorauth/types.d.ts @@ -7,6 +7,7 @@ import type { } from "../recipeModule/types"; import type { FC } from "react"; import type { OverrideableBuilder } from "supertokens-js-override"; +import type NormalisedURLPath from "supertokens-web-js/lib/build/normalisedURLPath"; import type { RecipeInterface } from "supertokens-web-js/recipe/multifactorauth"; import type { MFAFactorInfo } from "supertokens-web-js/recipe/multifactorauth/types"; export declare type ComponentOverrideMap = { @@ -63,7 +64,7 @@ export declare type FactorChooserThemeProps = { showBackButton: boolean; onBackButtonClicked: () => void; navigateToFactor: (factorId: string) => void; - logout: () => void; + onLogoutClicked: () => void; config: NormalisedConfig; userContext?: any; }; @@ -72,5 +73,5 @@ export declare type SecondaryFactorRedirectionInfo = { name: string; description: string; logo: FC; - path: string; + path: NormalisedURLPath; }; diff --git a/lib/build/recipe/session/components/features/accessDeniedScreen/index.d.ts b/lib/build/recipe/session/components/features/accessDeniedScreen/index.d.ts index 8e9166177..98add6532 100644 --- a/lib/build/recipe/session/components/features/accessDeniedScreen/index.d.ts +++ b/lib/build/recipe/session/components/features/accessDeniedScreen/index.d.ts @@ -6,6 +6,8 @@ declare const AccessDeniedScreen: React.FC< FeatureBaseProps & { recipe: Recipe; useComponentOverrides: () => ComponentOverrideMap; + useShadowDom?: boolean; + error?: string; } >; export default AccessDeniedScreen; diff --git a/lib/build/recipe/session/prebuiltui.d.ts b/lib/build/recipe/session/prebuiltui.d.ts index 01aacb148..cc68f20d5 100644 --- a/lib/build/recipe/session/prebuiltui.d.ts +++ b/lib/build/recipe/session/prebuiltui.d.ts @@ -13,7 +13,7 @@ export declare class SessionPreBuiltUI extends RecipeRouter { static getFeatureComponent( componentName: "accessDenied", props: FeatureBaseProps & { - redirectOnSessionExists?: boolean; + error?: string; userContext?: any; }, useComponentOverrides?: () => GenericComponentOverrideMap @@ -22,6 +22,8 @@ export declare class SessionPreBuiltUI extends RecipeRouter { getFeatureComponent: ( componentName: "accessDenied", props: FeatureBaseProps & { + useShadowDom?: boolean; + error?: string; userContext?: any; }, useComponentOverrides?: () => GenericComponentOverrideMap @@ -29,6 +31,8 @@ export declare class SessionPreBuiltUI extends RecipeRouter { static reset(): void; static AccessDeniedScreen: ( prop?: PropsWithChildren<{ + useShadowDom?: boolean; + error?: string; userContext?: any; }> ) => ReactElement; @@ -36,6 +40,8 @@ export declare class SessionPreBuiltUI extends RecipeRouter { } declare const AccessDeniedScreen: ( prop?: PropsWithChildren<{ + useShadowDom?: boolean; + error?: string; userContext?: any; }> ) => ReactElement; diff --git a/lib/build/recipe/session/types.d.ts b/lib/build/recipe/session/types.d.ts index f244200f8..409bfce83 100644 --- a/lib/build/recipe/session/types.d.ts +++ b/lib/build/recipe/session/types.d.ts @@ -11,11 +11,13 @@ export declare type RecipeEventWithSessionContext = RecipeEvent & { sessionContext: SessionContextUpdate; }; export declare type InputType = WebJSInputType & { + useShadowDom?: boolean; style?: string; accessDeniedScreen?: SessionFeatureBaseConfig; onHandleEvent?: (event: RecipeEventWithSessionContext) => void; }; export declare type NormalisedSessionConfig = NormalisedConfig & { + useShadowDom: boolean; accessDeniedScreen: NormalisedBaseConfig; override: { functions: ( @@ -43,6 +45,7 @@ export declare type SessionContextType = export declare type AccessDeniedThemeProps = { recipe: Session; history: any; + error?: string; config: NormalisedSessionConfig; }; export declare type ComponentOverrideMap = { diff --git a/lib/build/session-shared.js b/lib/build/session-shared.js index bd3b554b5..e19b2d6c0 100644 --- a/lib/build/session-shared.js +++ b/lib/build/session-shared.js @@ -1,10 +1,304 @@ "use strict"; var genericComponentOverrideContext = require("./genericComponentOverrideContext.js"); +var WebJSSessionRecipe = require("supertokens-web-js/recipe/session"); +var componentOverrideContext = require("./session-shared3.js"); +var recipe = require("./session-shared2.js"); +var uiEntry = require("./index2.js"); +var React = require("react"); -var _a = genericComponentOverrideContext.createGenericComponentsOverrideContext(), - useContext = _a[0], - Provider = _a[1]; +function _interopDefault(e) { + return e && e.__esModule ? e : { default: e }; +} -exports.Provider = Provider; -exports.useContext = useContext; +var React__default = /*#__PURE__*/ _interopDefault(React); + +var BooleanClaim = /** @class */ (function (_super) { + genericComponentOverrideContext.__extends(BooleanClaim, _super); + function BooleanClaim(config) { + var _this = _super.call(this, config) || this; + var validatorsWithCallbacks = genericComponentOverrideContext.__assign({}, _this.validators); + var _loop_1 = function (key) { + var validator = validatorsWithCallbacks[key]; + validatorsWithCallbacks[key] = function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + return genericComponentOverrideContext.__assign( + genericComponentOverrideContext.__assign({}, validator.apply(void 0, args)), + { + onFailureRedirection: config.onFailureRedirection, + showAccessDeniedOnFailure: config.showAccessDeniedOnFailure, + } + ); + }; + }; + for (var key in validatorsWithCallbacks) { + _loop_1(key); + } + _this.validators = validatorsWithCallbacks; + return _this; + } + return BooleanClaim; +})(WebJSSessionRecipe.BooleanClaim); + +var PrimitiveArrayClaim = /** @class */ (function (_super) { + genericComponentOverrideContext.__extends(PrimitiveArrayClaim, _super); + function PrimitiveArrayClaim(config) { + var _this = _super.call(this, config) || this; + var validatorsWithCallbacks = genericComponentOverrideContext.__assign({}, _this.validators); + var _loop_1 = function (key) { + var validator = validatorsWithCallbacks[key]; + validatorsWithCallbacks[key] = function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + return genericComponentOverrideContext.__assign( + genericComponentOverrideContext.__assign({}, validator.apply(void 0, args)), + { + onFailureRedirection: config.onFailureRedirection, + showAccessDeniedOnFailure: config.showAccessDeniedOnFailure, + } + ); + }; + }; + for (var key in validatorsWithCallbacks) { + _loop_1(key); + } + _this.validators = validatorsWithCallbacks; + return _this; + } + return PrimitiveArrayClaim; +})(WebJSSessionRecipe.PrimitiveArrayClaim); + +var PrimitiveClaim = /** @class */ (function (_super) { + genericComponentOverrideContext.__extends(PrimitiveClaim, _super); + function PrimitiveClaim(config) { + var _this = _super.call(this, config) || this; + var validatorsWithCallbacks = genericComponentOverrideContext.__assign({}, _this.validators); + var _loop_1 = function (key) { + var validator = validatorsWithCallbacks[key]; + validatorsWithCallbacks[key] = function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + return genericComponentOverrideContext.__assign( + genericComponentOverrideContext.__assign({}, validator.apply(void 0, args)), + { + onFailureRedirection: config.onFailureRedirection, + showAccessDeniedOnFailure: config.showAccessDeniedOnFailure, + } + ); + }; + }; + for (var key in validatorsWithCallbacks) { + _loop_1(key); + } + _this.validators = validatorsWithCallbacks; + return _this; + } + return PrimitiveClaim; +})(WebJSSessionRecipe.PrimitiveClaim); + +var useSessionContext$1 = function () { + var ctx = React__default.default.useContext(uiEntry.SessionContext); + if (ctx.isDefault === true) { + throw new Error("Cannot use useSessionContext outside auth wrapper components."); + } + return ctx; +}; + +var useClaimValue$1 = function (claim) { + var ctx = useSessionContext$1(); + if (ctx.loading) { + return { + loading: true, + }; + } + if (ctx.doesSessionExist === false) { + return { + loading: false, + doesSessionExist: false, + value: undefined, + }; + } + return { + loading: false, + doesSessionExist: true, + value: claim.getValueFromPayload(ctx.accessTokenPayload), + }; +}; + +/* 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 SessionAPIWrapper = /** @class */ (function () { + function SessionAPIWrapper() {} + SessionAPIWrapper.init = function (config) { + return recipe.Session.init(config); + }; + SessionAPIWrapper.getUserId = function (input) { + return genericComponentOverrideContext.__awaiter(this, void 0, void 0, function () { + return genericComponentOverrideContext.__generator(this, function (_a) { + return [ + 2 /*return*/, + recipe.Session.getInstanceOrThrow().getUserId({ + userContext: genericComponentOverrideContext.getNormalisedUserContext( + input === null || input === void 0 ? void 0 : input.userContext + ), + }), + ]; + }); + }); + }; + SessionAPIWrapper.getAccessToken = function (input) { + return genericComponentOverrideContext.__awaiter(this, void 0, void 0, function () { + return genericComponentOverrideContext.__generator(this, function (_a) { + return [ + 2 /*return*/, + recipe.Session.getInstanceOrThrow().getAccessToken({ + userContext: genericComponentOverrideContext.getNormalisedUserContext( + input === null || input === void 0 ? void 0 : input.userContext + ), + }), + ]; + }); + }); + }; + SessionAPIWrapper.getAccessTokenPayloadSecurely = function (input) { + return genericComponentOverrideContext.__awaiter(this, void 0, void 0, function () { + return genericComponentOverrideContext.__generator(this, function (_a) { + return [ + 2 /*return*/, + recipe.Session.getInstanceOrThrow().getAccessTokenPayloadSecurely({ + userContext: genericComponentOverrideContext.getNormalisedUserContext( + input === null || input === void 0 ? void 0 : input.userContext + ), + }), + ]; + }); + }); + }; + SessionAPIWrapper.attemptRefreshingSession = function () { + return genericComponentOverrideContext.__awaiter(this, void 0, void 0, function () { + return genericComponentOverrideContext.__generator(this, function (_a) { + return [2 /*return*/, recipe.Session.getInstanceOrThrow().attemptRefreshingSession()]; + }); + }); + }; + SessionAPIWrapper.doesSessionExist = function (input) { + return genericComponentOverrideContext.__awaiter(this, void 0, void 0, function () { + return genericComponentOverrideContext.__generator(this, function (_a) { + return [ + 2 /*return*/, + recipe.Session.getInstanceOrThrow().doesSessionExist({ + userContext: genericComponentOverrideContext.getNormalisedUserContext( + input === null || input === void 0 ? void 0 : input.userContext + ), + }), + ]; + }); + }); + }; + /** + * @deprecated + */ + SessionAPIWrapper.addAxiosInterceptors = function (axiosInstance, userContext) { + return recipe.Session.addAxiosInterceptors( + axiosInstance, + genericComponentOverrideContext.getNormalisedUserContext(userContext) + ); + }; + SessionAPIWrapper.signOut = function (input) { + return genericComponentOverrideContext.__awaiter(this, void 0, void 0, function () { + return genericComponentOverrideContext.__generator(this, function (_a) { + return [ + 2 /*return*/, + recipe.Session.getInstanceOrThrow().signOut({ + userContext: genericComponentOverrideContext.getNormalisedUserContext( + input === null || input === void 0 ? void 0 : input.userContext + ), + }), + ]; + }); + }); + }; + SessionAPIWrapper.validateClaims = function (input) { + return recipe.Session.getInstanceOrThrow().validateClaims({ + overrideGlobalClaimValidators: + input === null || input === void 0 ? void 0 : input.overrideGlobalClaimValidators, + userContext: genericComponentOverrideContext.getNormalisedUserContext( + input === null || input === void 0 ? void 0 : input.userContext + ), + }); + }; + SessionAPIWrapper.getInvalidClaimsFromResponse = function (input) { + return recipe.Session.getInstanceOrThrow().getInvalidClaimsFromResponse(input); + }; + SessionAPIWrapper.getClaimValue = function (input) { + return recipe.Session.getInstanceOrThrow().getClaimValue({ + claim: input.claim, + userContext: genericComponentOverrideContext.getNormalisedUserContext( + input === null || input === void 0 ? void 0 : input.userContext + ), + }); + }; + SessionAPIWrapper.useSessionContext = useSessionContext$1; + SessionAPIWrapper.useClaimValue = useClaimValue$1; + SessionAPIWrapper.SessionAuth = uiEntry.SessionAuthWrapper; + SessionAPIWrapper.ComponentsOverrideProvider = componentOverrideContext.Provider; + return SessionAPIWrapper; +})(); +var useSessionContext = SessionAPIWrapper.useSessionContext; +var useClaimValue = SessionAPIWrapper.useClaimValue; +var SessionAuth = SessionAPIWrapper.SessionAuth; +var init = SessionAPIWrapper.init; +var getUserId = SessionAPIWrapper.getUserId; +var getAccessToken = SessionAPIWrapper.getAccessToken; +var getAccessTokenPayloadSecurely = SessionAPIWrapper.getAccessTokenPayloadSecurely; +var attemptRefreshingSession = SessionAPIWrapper.attemptRefreshingSession; +var doesSessionExist = SessionAPIWrapper.doesSessionExist; +/** + * @deprecated + */ +var addAxiosInterceptors = SessionAPIWrapper.addAxiosInterceptors; +var signOut = SessionAPIWrapper.signOut; +var validateClaims = SessionAPIWrapper.validateClaims; +var getInvalidClaimsFromResponse = SessionAPIWrapper.getInvalidClaimsFromResponse; +var getClaimValue = SessionAPIWrapper.getClaimValue; +var SessionComponentsOverrideProvider = SessionAPIWrapper.ComponentsOverrideProvider; + +exports.BooleanClaim = BooleanClaim; +exports.PrimitiveArrayClaim = PrimitiveArrayClaim; +exports.PrimitiveClaim = PrimitiveClaim; +exports.SessionAPIWrapper = SessionAPIWrapper; +exports.SessionAuth = SessionAuth; +exports.SessionComponentsOverrideProvider = SessionComponentsOverrideProvider; +exports.addAxiosInterceptors = addAxiosInterceptors; +exports.attemptRefreshingSession = attemptRefreshingSession; +exports.doesSessionExist = doesSessionExist; +exports.getAccessToken = getAccessToken; +exports.getAccessTokenPayloadSecurely = getAccessTokenPayloadSecurely; +exports.getClaimValue = getClaimValue; +exports.getInvalidClaimsFromResponse = getInvalidClaimsFromResponse; +exports.getUserId = getUserId; +exports.init = init; +exports.signOut = signOut; +exports.useClaimValue = useClaimValue; +exports.useSessionContext = useSessionContext$1; +exports.useSessionContext$1 = useSessionContext; +exports.validateClaims = validateClaims; diff --git a/lib/build/session-shared3.js b/lib/build/session-shared3.js index c9e7ddfa8..bd3b554b5 100644 --- a/lib/build/session-shared3.js +++ b/lib/build/session-shared3.js @@ -1,304 +1,10 @@ "use strict"; var genericComponentOverrideContext = require("./genericComponentOverrideContext.js"); -var WebJSSessionRecipe = require("supertokens-web-js/recipe/session"); -var componentOverrideContext = require("./session-shared.js"); -var recipe = require("./session-shared2.js"); -var uiEntry = require("./index2.js"); -var React = require("react"); -function _interopDefault(e) { - return e && e.__esModule ? e : { default: e }; -} +var _a = genericComponentOverrideContext.createGenericComponentsOverrideContext(), + useContext = _a[0], + Provider = _a[1]; -var React__default = /*#__PURE__*/ _interopDefault(React); - -var BooleanClaim = /** @class */ (function (_super) { - genericComponentOverrideContext.__extends(BooleanClaim, _super); - function BooleanClaim(config) { - var _this = _super.call(this, config) || this; - var validatorsWithCallbacks = genericComponentOverrideContext.__assign({}, _this.validators); - var _loop_1 = function (key) { - var validator = validatorsWithCallbacks[key]; - validatorsWithCallbacks[key] = function () { - var args = []; - for (var _i = 0; _i < arguments.length; _i++) { - args[_i] = arguments[_i]; - } - return genericComponentOverrideContext.__assign( - genericComponentOverrideContext.__assign({}, validator.apply(void 0, args)), - { - onFailureRedirection: config.onFailureRedirection, - showAccessDeniedOnFailure: config.showAccessDeniedOnFailure, - } - ); - }; - }; - for (var key in validatorsWithCallbacks) { - _loop_1(key); - } - _this.validators = validatorsWithCallbacks; - return _this; - } - return BooleanClaim; -})(WebJSSessionRecipe.BooleanClaim); - -var PrimitiveArrayClaim = /** @class */ (function (_super) { - genericComponentOverrideContext.__extends(PrimitiveArrayClaim, _super); - function PrimitiveArrayClaim(config) { - var _this = _super.call(this, config) || this; - var validatorsWithCallbacks = genericComponentOverrideContext.__assign({}, _this.validators); - var _loop_1 = function (key) { - var validator = validatorsWithCallbacks[key]; - validatorsWithCallbacks[key] = function () { - var args = []; - for (var _i = 0; _i < arguments.length; _i++) { - args[_i] = arguments[_i]; - } - return genericComponentOverrideContext.__assign( - genericComponentOverrideContext.__assign({}, validator.apply(void 0, args)), - { - onFailureRedirection: config.onFailureRedirection, - showAccessDeniedOnFailure: config.showAccessDeniedOnFailure, - } - ); - }; - }; - for (var key in validatorsWithCallbacks) { - _loop_1(key); - } - _this.validators = validatorsWithCallbacks; - return _this; - } - return PrimitiveArrayClaim; -})(WebJSSessionRecipe.PrimitiveArrayClaim); - -var PrimitiveClaim = /** @class */ (function (_super) { - genericComponentOverrideContext.__extends(PrimitiveClaim, _super); - function PrimitiveClaim(config) { - var _this = _super.call(this, config) || this; - var validatorsWithCallbacks = genericComponentOverrideContext.__assign({}, _this.validators); - var _loop_1 = function (key) { - var validator = validatorsWithCallbacks[key]; - validatorsWithCallbacks[key] = function () { - var args = []; - for (var _i = 0; _i < arguments.length; _i++) { - args[_i] = arguments[_i]; - } - return genericComponentOverrideContext.__assign( - genericComponentOverrideContext.__assign({}, validator.apply(void 0, args)), - { - onFailureRedirection: config.onFailureRedirection, - showAccessDeniedOnFailure: config.showAccessDeniedOnFailure, - } - ); - }; - }; - for (var key in validatorsWithCallbacks) { - _loop_1(key); - } - _this.validators = validatorsWithCallbacks; - return _this; - } - return PrimitiveClaim; -})(WebJSSessionRecipe.PrimitiveClaim); - -var useSessionContext$1 = function () { - var ctx = React__default.default.useContext(uiEntry.SessionContext); - if (ctx.isDefault === true) { - throw new Error("Cannot use useSessionContext outside auth wrapper components."); - } - return ctx; -}; - -var useClaimValue$1 = function (claim) { - var ctx = useSessionContext$1(); - if (ctx.loading) { - return { - loading: true, - }; - } - if (ctx.doesSessionExist === false) { - return { - loading: false, - doesSessionExist: false, - value: undefined, - }; - } - return { - loading: false, - doesSessionExist: true, - value: claim.getValueFromPayload(ctx.accessTokenPayload), - }; -}; - -/* 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 SessionAPIWrapper = /** @class */ (function () { - function SessionAPIWrapper() {} - SessionAPIWrapper.init = function (config) { - return recipe.Session.init(config); - }; - SessionAPIWrapper.getUserId = function (input) { - return genericComponentOverrideContext.__awaiter(this, void 0, void 0, function () { - return genericComponentOverrideContext.__generator(this, function (_a) { - return [ - 2 /*return*/, - recipe.Session.getInstanceOrThrow().getUserId({ - userContext: genericComponentOverrideContext.getNormalisedUserContext( - input === null || input === void 0 ? void 0 : input.userContext - ), - }), - ]; - }); - }); - }; - SessionAPIWrapper.getAccessToken = function (input) { - return genericComponentOverrideContext.__awaiter(this, void 0, void 0, function () { - return genericComponentOverrideContext.__generator(this, function (_a) { - return [ - 2 /*return*/, - recipe.Session.getInstanceOrThrow().getAccessToken({ - userContext: genericComponentOverrideContext.getNormalisedUserContext( - input === null || input === void 0 ? void 0 : input.userContext - ), - }), - ]; - }); - }); - }; - SessionAPIWrapper.getAccessTokenPayloadSecurely = function (input) { - return genericComponentOverrideContext.__awaiter(this, void 0, void 0, function () { - return genericComponentOverrideContext.__generator(this, function (_a) { - return [ - 2 /*return*/, - recipe.Session.getInstanceOrThrow().getAccessTokenPayloadSecurely({ - userContext: genericComponentOverrideContext.getNormalisedUserContext( - input === null || input === void 0 ? void 0 : input.userContext - ), - }), - ]; - }); - }); - }; - SessionAPIWrapper.attemptRefreshingSession = function () { - return genericComponentOverrideContext.__awaiter(this, void 0, void 0, function () { - return genericComponentOverrideContext.__generator(this, function (_a) { - return [2 /*return*/, recipe.Session.getInstanceOrThrow().attemptRefreshingSession()]; - }); - }); - }; - SessionAPIWrapper.doesSessionExist = function (input) { - return genericComponentOverrideContext.__awaiter(this, void 0, void 0, function () { - return genericComponentOverrideContext.__generator(this, function (_a) { - return [ - 2 /*return*/, - recipe.Session.getInstanceOrThrow().doesSessionExist({ - userContext: genericComponentOverrideContext.getNormalisedUserContext( - input === null || input === void 0 ? void 0 : input.userContext - ), - }), - ]; - }); - }); - }; - /** - * @deprecated - */ - SessionAPIWrapper.addAxiosInterceptors = function (axiosInstance, userContext) { - return recipe.Session.addAxiosInterceptors( - axiosInstance, - genericComponentOverrideContext.getNormalisedUserContext(userContext) - ); - }; - SessionAPIWrapper.signOut = function (input) { - return genericComponentOverrideContext.__awaiter(this, void 0, void 0, function () { - return genericComponentOverrideContext.__generator(this, function (_a) { - return [ - 2 /*return*/, - recipe.Session.getInstanceOrThrow().signOut({ - userContext: genericComponentOverrideContext.getNormalisedUserContext( - input === null || input === void 0 ? void 0 : input.userContext - ), - }), - ]; - }); - }); - }; - SessionAPIWrapper.validateClaims = function (input) { - return recipe.Session.getInstanceOrThrow().validateClaims({ - overrideGlobalClaimValidators: - input === null || input === void 0 ? void 0 : input.overrideGlobalClaimValidators, - userContext: genericComponentOverrideContext.getNormalisedUserContext( - input === null || input === void 0 ? void 0 : input.userContext - ), - }); - }; - SessionAPIWrapper.getInvalidClaimsFromResponse = function (input) { - return recipe.Session.getInstanceOrThrow().getInvalidClaimsFromResponse(input); - }; - SessionAPIWrapper.getClaimValue = function (input) { - return recipe.Session.getInstanceOrThrow().getClaimValue({ - claim: input.claim, - userContext: genericComponentOverrideContext.getNormalisedUserContext( - input === null || input === void 0 ? void 0 : input.userContext - ), - }); - }; - SessionAPIWrapper.useSessionContext = useSessionContext$1; - SessionAPIWrapper.useClaimValue = useClaimValue$1; - SessionAPIWrapper.SessionAuth = uiEntry.SessionAuthWrapper; - SessionAPIWrapper.ComponentsOverrideProvider = componentOverrideContext.Provider; - return SessionAPIWrapper; -})(); -var useSessionContext = SessionAPIWrapper.useSessionContext; -var useClaimValue = SessionAPIWrapper.useClaimValue; -var SessionAuth = SessionAPIWrapper.SessionAuth; -var init = SessionAPIWrapper.init; -var getUserId = SessionAPIWrapper.getUserId; -var getAccessToken = SessionAPIWrapper.getAccessToken; -var getAccessTokenPayloadSecurely = SessionAPIWrapper.getAccessTokenPayloadSecurely; -var attemptRefreshingSession = SessionAPIWrapper.attemptRefreshingSession; -var doesSessionExist = SessionAPIWrapper.doesSessionExist; -/** - * @deprecated - */ -var addAxiosInterceptors = SessionAPIWrapper.addAxiosInterceptors; -var signOut = SessionAPIWrapper.signOut; -var validateClaims = SessionAPIWrapper.validateClaims; -var getInvalidClaimsFromResponse = SessionAPIWrapper.getInvalidClaimsFromResponse; -var getClaimValue = SessionAPIWrapper.getClaimValue; -var SessionComponentsOverrideProvider = SessionAPIWrapper.ComponentsOverrideProvider; - -exports.BooleanClaim = BooleanClaim; -exports.PrimitiveArrayClaim = PrimitiveArrayClaim; -exports.PrimitiveClaim = PrimitiveClaim; -exports.SessionAPIWrapper = SessionAPIWrapper; -exports.SessionAuth = SessionAuth; -exports.SessionComponentsOverrideProvider = SessionComponentsOverrideProvider; -exports.addAxiosInterceptors = addAxiosInterceptors; -exports.attemptRefreshingSession = attemptRefreshingSession; -exports.doesSessionExist = doesSessionExist; -exports.getAccessToken = getAccessToken; -exports.getAccessTokenPayloadSecurely = getAccessTokenPayloadSecurely; -exports.getClaimValue = getClaimValue; -exports.getInvalidClaimsFromResponse = getInvalidClaimsFromResponse; -exports.getUserId = getUserId; -exports.init = init; -exports.signOut = signOut; -exports.useClaimValue = useClaimValue; -exports.useSessionContext = useSessionContext$1; -exports.useSessionContext$1 = useSessionContext; -exports.validateClaims = validateClaims; +exports.Provider = Provider; +exports.useContext = useContext; diff --git a/lib/build/session.js b/lib/build/session.js index c109ea077..1b65118a2 100644 --- a/lib/build/session.js +++ b/lib/build/session.js @@ -3,8 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true }); require("./genericComponentOverrideContext.js"); -var session = require("./session-shared3.js"); -require("./session-shared.js"); +var session = require("./session-shared.js"); +require("./session-shared3.js"); require("./session-shared2.js"); var uiEntry = require("./index2.js"); require("supertokens-web-js"); diff --git a/lib/build/sessionprebuiltui.js b/lib/build/sessionprebuiltui.js index 229b076ee..ed1aac330 100644 --- a/lib/build/sessionprebuiltui.js +++ b/lib/build/sessionprebuiltui.js @@ -3,7 +3,7 @@ var genericComponentOverrideContext = require("./genericComponentOverrideContext.js"); var jsxRuntime = require("react/jsx-runtime"); var uiEntry = require("./index2.js"); -var componentOverrideContext = require("./session-shared.js"); +var componentOverrideContext = require("./session-shared3.js"); var windowHandler = require("supertokens-web-js/utils/windowHandler"); var translations = require("./translations.js"); var translationContext = require("./translationContext.js"); @@ -82,7 +82,7 @@ function LogoutButton(_a) { } var styles = - '/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.\n *\n * This software is licensed under the Apache License, Version 2.0 (the\n * "License") as published by the Apache Software Foundation.\n *\n * You may not use this file except in compliance with the License. You may\n * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations\n * under the License.\n */\n\n[data-supertokens~="container"] {\n --palette-background: 255, 255, 255;\n --palette-inputBackground: 250, 250, 250;\n --palette-inputBorder: 224, 224, 224;\n --palette-primary: 255, 155, 51;\n --palette-primaryBorder: 238, 141, 35;\n --palette-success: 65, 167, 0;\n --palette-successBackground: 217, 255, 191;\n --palette-error: 255, 23, 23;\n --palette-errorBackground: 255, 241, 235;\n --palette-textTitle: 34, 34, 34;\n --palette-textLabel: 34, 34, 34;\n --palette-textInput: 34, 34, 34;\n --palette-textPrimary: 101, 101, 101;\n --palette-textLink: 0, 118, 255;\n --palette-buttonText: 255, 255, 255;\n --palette-textGray: 128, 128, 128;\n --palette-superTokensBrandingBackground: 242, 245, 246;\n --palette-superTokensBrandingText: 173, 189, 196;\n\n --font-size-0: 12px;\n --font-size-1: 14px;\n --font-size-2: 16px;\n --font-size-3: 19px;\n --font-size-4: 24px;\n}\n\n/*\n * Default styles.\n */\n\n@-webkit-keyframes slideTop {\n 0% {\n -webkit-transform: translateY(-5px);\n transform: translateY(-5px);\n }\n 100% {\n -webkit-transform: translateY(0px);\n transform: translateY(0px);\n }\n}\n\n@keyframes slideTop {\n 0% {\n -webkit-transform: translateY(-5px);\n transform: translateY(-5px);\n }\n 100% {\n -webkit-transform: translateY(0px);\n transform: translateY(0px);\n }\n}\n\n@-webkit-keyframes swing-in-top-fwd {\n 0% {\n -webkit-transform: rotateX(-100deg);\n transform: rotateX(-100deg);\n -webkit-transform-origin: top;\n transform-origin: top;\n opacity: 0;\n }\n 100% {\n -webkit-transform: rotateX(0deg);\n transform: rotateX(0deg);\n -webkit-transform-origin: top;\n transform-origin: top;\n opacity: 1;\n }\n}\n\n@keyframes swing-in-top-fwd {\n 0% {\n -webkit-transform: rotateX(-100deg);\n transform: rotateX(-100deg);\n -webkit-transform-origin: top;\n transform-origin: top;\n opacity: 0;\n }\n 100% {\n -webkit-transform: rotateX(0deg);\n transform: rotateX(0deg);\n -webkit-transform-origin: top;\n transform-origin: top;\n opacity: 1;\n }\n}\n\n[data-supertokens~="container"] {\n font-family: "Rubik", sans-serif;\n margin: 12px auto;\n margin-top: 26px;\n margin-bottom: 26px;\n width: 420px;\n text-align: center;\n border-radius: 8px;\n box-shadow: 1px 1px 10px rgba(0, 0, 0, 0.16);\n background-color: rgb(var(--palette-background));\n}\n\n@media (max-width: 440px) {\n [data-supertokens~="container"] {\n width: 95vw;\n }\n}\n\n[data-supertokens~="row"] {\n margin: 0 auto;\n width: 76%;\n padding-top: 30px;\n padding-bottom: 10px;\n}\n\n[data-supertokens~="superTokensBranding"] {\n display: block;\n margin: 0 auto;\n background: rgb(var(--palette-superTokensBrandingBackground));\n color: rgb(var(--palette-superTokensBrandingText));\n text-decoration: none;\n width: -webkit-fit-content;\n width: -moz-fit-content;\n width: fit-content;\n border-radius: 6px 6px 0 0;\n padding: 4px 9px;\n font-weight: 300;\n font-size: var(--font-size-0);\n letter-spacing: 0.4px;\n}\n\n[data-supertokens~="generalError"] {\n background: rgb(var(--palette-errorBackground));\n padding-top: 10px;\n padding-bottom: 10px;\n margin-bottom: 15px;\n padding-left: 18px;\n padding-right: 18px;\n letter-spacing: 0.2px;\n font-size: var(--font-size-1);\n border-radius: 8px;\n color: rgb(var(--palette-error));\n -webkit-animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;\n animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;\n word-wrap: break-word;\n}\n\n[data-supertokens~="headerTitle"] {\n font-size: var(--font-size-4);\n line-height: 40px;\n letter-spacing: 0.58px;\n font-weight: 800;\n margin-bottom: 2px;\n color: rgb(var(--palette-textTitle));\n}\n\n[data-supertokens~="headerSubtitle"] {\n margin-bottom: 21px;\n}\n\n[data-supertokens~="privacyPolicyAndTermsAndConditions"] {\n max-width: 300px;\n margin-top: 10px;\n}\n\n[data-supertokens~="privacyPolicyAndTermsAndConditions"] a {\n line-height: 21px;\n}\n\n/* TODO: split the link style into separate things*/\n\n/* We add this before primary and secondary text, because if they are applied to the same element the other ones take priority */\n\n[data-supertokens~="link"] {\n padding-left: 3px;\n padding-right: 3px;\n color: rgb(var(--palette-textLink));\n font-size: var(--font-size-1);\n cursor: pointer;\n letter-spacing: 0.16px;\n line-height: 26px;\n}\n\n[data-supertokens~="primaryText"] {\n font-size: var(--font-size-1);\n font-weight: 500;\n letter-spacing: 0.4px;\n line-height: 21px;\n color: rgb(var(--palette-textLabel));\n}\n\n[data-supertokens~="secondaryText"] {\n font-size: var(--font-size-1);\n font-weight: 300;\n letter-spacing: 0.4px;\n color: rgb(var(--palette-textPrimary));\n}\n\n[data-supertokens~="divider"] {\n margin-top: 1em;\n margin-bottom: 1em;\n border-bottom: 0.3px solid #dddddd;\n align-items: center;\n padding-bottom: 5px;\n}\n\n[data-supertokens~="headerTinyTitle"] {\n margin-top: 13px;\n font-size: var(--font-size-3);\n letter-spacing: 1.1px;\n font-weight: 500;\n line-height: 28px;\n}\n\n[data-supertokens~="secondaryLinkWithArrow"] {\n margin-top: 10px;\n margin-bottom: 30px;\n cursor: pointer;\n}\n\n[data-supertokens~="secondaryLinkWithArrow"]:hover {\n position: relative;\n left: 2px;\n word-spacing: 4px;\n}\n\n[data-supertokens~="generalSuccess"] {\n color: rgb(var(--palette-success));\n font-size: var(--font-size-1);\n background: rgb(var(--palette-successBackground));\n -webkit-animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;\n animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;\n padding: 9px 15px 9px 15px;\n border-radius: 6px;\n display: inline-block;\n}\n\n[data-supertokens~="spinner"] {\n width: 80px;\n height: auto;\n padding-top: 20px;\n padding-bottom: 40px;\n margin: 0 auto;\n}\n\n[data-supertokens~="error"] {\n color: rgb(var(--palette-error));\n}\n\n[data-supertokens~="linkButton"] {\n background-color: transparent;\n border: 0;\n}\n\n[data-supertokens~="secondaryLinkWithLeftArrow"] {\n margin-top: 10px;\n margin-bottom: 40px;\n cursor: pointer;\n}\n\n[data-supertokens~="secondaryLinkWithLeftArrow"] svg {\n margin-right: 0.3em;\n}\n\n[data-supertokens~="secondaryLinkWithLeftArrow"]:hover svg {\n position: relative;\n left: -4px;\n}\n\n[data-supertokens~="button"] {\n background-color: rgb(var(--palette-primary));\n color: rgb(var(--palette-buttonText));\n width: 100%;\n height: 34px;\n font-weight: 700;\n border-width: 1px;\n border-style: solid;\n border-radius: 6px;\n border-color: rgb(var(--palette-primaryBorder));\n background-position: center;\n transition: all 0.4s;\n background-size: 12000%;\n cursor: pointer;\n}\n\n[data-supertokens~="button"]:disabled {\n border: none;\n cursor: no-drop;\n}\n\n[data-supertokens~="button"]:active {\n outline: none;\n transition: all 0s;\n background-size: 100%;\n -webkit-filter: brightness(0.85);\n filter: brightness(0.85);\n}\n\n[data-supertokens~="button"]:focus {\n outline: none;\n}\n\n/* Override */\n\n[data-supertokens~="row"] {\n padding-top: 32px;\n padding-bottom: 32px;\n}\n\n[data-supertokens~="divider"] {\n padding: 0;\n margin: 24px 0;\n}\n\n[data-supertokens~="headerTitle"] {\n padding-top: 24px;\n font-style: normal;\n font-weight: 700;\n font-size: 20px;\n line-height: 30px;\n}\n\n/* Override end */\n\n[data-supertokens~="center"] {\n height: 100%;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n flex: 1 1 auto;\n}\n\n[data-supertokens~="buttonsGroup"] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n\n[data-supertokens~="buttonBase"] {\n font-size: var(--font-size-1);\n line-height: 21px;\n font-weight: 500;\n background: transparent;\n outline: none;\n border: none;\n cursor: pointer;\n}\n\n[data-supertokens~="backButton"] {\n color: rgb(var(--palette-textLink));\n}\n\n[data-supertokens~="logoutButton"] {\n display: flex;\n align-items: center;\n gap: 6px;\n color: rgb(var(--palette-textGray));\n}\n'; + '/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.\n *\n * This software is licensed under the Apache License, Version 2.0 (the\n * "License") as published by the Apache Software Foundation.\n *\n * You may not use this file except in compliance with the License. You may\n * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations\n * under the License.\n */\n\n[data-supertokens~="container"] {\n --palette-background: 255, 255, 255;\n --palette-inputBackground: 250, 250, 250;\n --palette-inputBorder: 224, 224, 224;\n --palette-primary: 255, 155, 51;\n --palette-primaryBorder: 238, 141, 35;\n --palette-success: 65, 167, 0;\n --palette-successBackground: 217, 255, 191;\n --palette-error: 255, 23, 23;\n --palette-errorBackground: 255, 241, 235;\n --palette-textTitle: 34, 34, 34;\n --palette-textLabel: 34, 34, 34;\n --palette-textInput: 34, 34, 34;\n --palette-textPrimary: 101, 101, 101;\n --palette-textLink: 0, 118, 255;\n --palette-buttonText: 255, 255, 255;\n --palette-textGray: 128, 128, 128;\n --palette-superTokensBrandingBackground: 242, 245, 246;\n --palette-superTokensBrandingText: 173, 189, 196;\n\n --font-size-0: 12px;\n --font-size-1: 14px;\n --font-size-2: 16px;\n --font-size-3: 19px;\n --font-size-4: 24px;\n}\n\n/*\n * Default styles.\n */\n\n@-webkit-keyframes slideTop {\n 0% {\n -webkit-transform: translateY(-5px);\n transform: translateY(-5px);\n }\n 100% {\n -webkit-transform: translateY(0px);\n transform: translateY(0px);\n }\n}\n\n@keyframes slideTop {\n 0% {\n -webkit-transform: translateY(-5px);\n transform: translateY(-5px);\n }\n 100% {\n -webkit-transform: translateY(0px);\n transform: translateY(0px);\n }\n}\n\n@-webkit-keyframes swing-in-top-fwd {\n 0% {\n -webkit-transform: rotateX(-100deg);\n transform: rotateX(-100deg);\n -webkit-transform-origin: top;\n transform-origin: top;\n opacity: 0;\n }\n 100% {\n -webkit-transform: rotateX(0deg);\n transform: rotateX(0deg);\n -webkit-transform-origin: top;\n transform-origin: top;\n opacity: 1;\n }\n}\n\n@keyframes swing-in-top-fwd {\n 0% {\n -webkit-transform: rotateX(-100deg);\n transform: rotateX(-100deg);\n -webkit-transform-origin: top;\n transform-origin: top;\n opacity: 0;\n }\n 100% {\n -webkit-transform: rotateX(0deg);\n transform: rotateX(0deg);\n -webkit-transform-origin: top;\n transform-origin: top;\n opacity: 1;\n }\n}\n\n[data-supertokens~="container"] {\n font-family: "Rubik", sans-serif;\n margin: 12px auto;\n margin-top: 26px;\n margin-bottom: 26px;\n width: 420px;\n text-align: center;\n border-radius: 8px;\n box-shadow: 1px 1px 10px rgba(0, 0, 0, 0.16);\n background-color: rgb(var(--palette-background));\n}\n\n@media (max-width: 440px) {\n [data-supertokens~="container"] {\n width: 95vw;\n }\n}\n\n[data-supertokens~="row"] {\n margin: 0 auto;\n width: 76%;\n padding-top: 30px;\n padding-bottom: 10px;\n}\n\n[data-supertokens~="superTokensBranding"] {\n display: block;\n margin: 0 auto;\n background: rgb(var(--palette-superTokensBrandingBackground));\n color: rgb(var(--palette-superTokensBrandingText));\n text-decoration: none;\n width: -webkit-fit-content;\n width: -moz-fit-content;\n width: fit-content;\n border-radius: 6px 6px 0 0;\n padding: 4px 9px;\n font-weight: 300;\n font-size: var(--font-size-0);\n letter-spacing: 0.4px;\n}\n\n[data-supertokens~="generalError"] {\n background: rgb(var(--palette-errorBackground));\n padding-top: 10px;\n padding-bottom: 10px;\n margin-bottom: 15px;\n padding-left: 18px;\n padding-right: 18px;\n letter-spacing: 0.2px;\n font-size: var(--font-size-1);\n border-radius: 8px;\n color: rgb(var(--palette-error));\n -webkit-animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;\n animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;\n word-wrap: break-word;\n}\n\n[data-supertokens~="headerTitle"] {\n font-size: var(--font-size-4);\n line-height: 40px;\n letter-spacing: 0.58px;\n font-weight: 800;\n margin-bottom: 2px;\n color: rgb(var(--palette-textTitle));\n}\n\n[data-supertokens~="headerSubtitle"] {\n margin-bottom: 21px;\n}\n\n[data-supertokens~="privacyPolicyAndTermsAndConditions"] {\n max-width: 300px;\n margin-top: 10px;\n}\n\n[data-supertokens~="privacyPolicyAndTermsAndConditions"] a {\n line-height: 21px;\n}\n\n/* TODO: split the link style into separate things*/\n\n/* We add this before primary and secondary text, because if they are applied to the same element the other ones take priority */\n\n[data-supertokens~="link"] {\n padding-left: 3px;\n padding-right: 3px;\n color: rgb(var(--palette-textLink));\n font-size: var(--font-size-1);\n cursor: pointer;\n letter-spacing: 0.16px;\n line-height: 26px;\n}\n\n[data-supertokens~="primaryText"] {\n font-size: var(--font-size-1);\n font-weight: 500;\n letter-spacing: 0.4px;\n line-height: 21px;\n color: rgb(var(--palette-textLabel));\n}\n\n[data-supertokens~="secondaryText"] {\n font-size: var(--font-size-1);\n font-weight: 300;\n letter-spacing: 0.4px;\n color: rgb(var(--palette-textPrimary));\n}\n\n[data-supertokens~="divider"] {\n margin-top: 1em;\n margin-bottom: 1em;\n border-bottom: 0.3px solid #dddddd;\n align-items: center;\n padding-bottom: 5px;\n}\n\n[data-supertokens~="headerTinyTitle"] {\n margin-top: 13px;\n font-size: var(--font-size-3);\n letter-spacing: 1.1px;\n font-weight: 500;\n line-height: 28px;\n}\n\n[data-supertokens~="secondaryLinkWithArrow"] {\n margin-top: 10px;\n margin-bottom: 30px;\n cursor: pointer;\n}\n\n[data-supertokens~="secondaryLinkWithArrow"]:hover {\n position: relative;\n left: 2px;\n word-spacing: 4px;\n}\n\n[data-supertokens~="generalSuccess"] {\n color: rgb(var(--palette-success));\n font-size: var(--font-size-1);\n background: rgb(var(--palette-successBackground));\n -webkit-animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;\n animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;\n padding: 9px 15px 9px 15px;\n border-radius: 6px;\n display: inline-block;\n}\n\n[data-supertokens~="spinner"] {\n width: 80px;\n height: auto;\n padding-top: 20px;\n padding-bottom: 40px;\n margin: 0 auto;\n}\n\n[data-supertokens~="error"] {\n color: rgb(var(--palette-error));\n}\n\n[data-supertokens~="linkButton"] {\n background-color: transparent;\n border: 0;\n}\n\n[data-supertokens~="secondaryLinkWithLeftArrow"] {\n margin-top: 10px;\n margin-bottom: 40px;\n cursor: pointer;\n}\n\n[data-supertokens~="secondaryLinkWithLeftArrow"] svg {\n margin-right: 0.3em;\n}\n\n[data-supertokens~="secondaryLinkWithLeftArrow"]:hover svg {\n position: relative;\n left: -4px;\n}\n\n[data-supertokens~="button"] {\n background-color: rgb(var(--palette-primary));\n color: rgb(var(--palette-buttonText));\n width: 100%;\n height: 34px;\n font-weight: 700;\n border-width: 1px;\n border-style: solid;\n border-radius: 6px;\n border-color: rgb(var(--palette-primaryBorder));\n background-position: center;\n transition: all 0.4s;\n background-size: 12000%;\n cursor: pointer;\n}\n\n[data-supertokens~="button"]:disabled {\n border: none;\n cursor: no-drop;\n}\n\n[data-supertokens~="button"]:active {\n outline: none;\n transition: all 0s;\n background-size: 100%;\n -webkit-filter: brightness(0.85);\n filter: brightness(0.85);\n}\n\n[data-supertokens~="button"]:focus {\n outline: none;\n}\n\n/* Override */\n\n[data-supertokens~="row"] {\n padding-top: 32px;\n padding-bottom: 32px;\n}\n\n[data-supertokens~="divider"] {\n padding: 0;\n margin: 24px 0;\n}\n\n[data-supertokens~="headerTitle"] {\n padding-top: 24px;\n font-style: normal;\n font-weight: 700;\n font-size: 20px;\n line-height: 30px;\n}\n\n/* Override end */\n\n[data-supertokens~="center"] {\n height: 100%;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n flex: 1 1 auto;\n}\n\n[data-supertokens~="buttonsGroup"] {\n margin-top: 24px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n\n[data-supertokens~="buttonBase"] {\n font-size: var(--font-size-1);\n line-height: 21px;\n font-weight: 500;\n background: transparent;\n outline: none;\n border: none;\n cursor: pointer;\n}\n\n[data-supertokens~="backButton"] {\n color: rgb(var(--palette-textLink));\n}\n\n[data-supertokens~="logoutButton"] {\n display: flex;\n align-items: center;\n gap: 6px;\n color: rgb(var(--palette-textGray));\n}\n'; var ThemeBase = function (_a) { var children = _a.children, @@ -166,6 +166,14 @@ var AccessDeniedScreen$2 = function (props) { ) ), jsxRuntime.jsx("div", { "data-supertokens": "divider" }), + props.error && + jsxRuntime.jsxs( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "primaryText accessDeniedError" }, + { children: [" ", props.error] } + ) + ), jsxRuntime.jsxs( "div", genericComponentOverrideContext.__assign( @@ -216,7 +224,7 @@ var defaultTranslationsSession = { }; var AccessDeniedScreen$1 = function (props) { - var _a; + var _a, _b; var recipeComponentOverrides = props.useComponentOverrides(); var history = (_a = uiEntry.UI.getReactRouterDomWithCustomHistory()) === null || _a === void 0 @@ -230,12 +238,19 @@ var AccessDeniedScreen$1 = function (props) { children: jsxRuntime.jsx( uiEntry.FeatureWrapper, genericComponentOverrideContext.__assign( - { defaultStore: defaultTranslationsSession }, + { + defaultStore: defaultTranslationsSession, + useShadowDom: + (_b = props.useShadowDom) !== null && _b !== void 0 + ? _b + : props.recipe.config.useShadowDom, + }, { children: jsxRuntime.jsx(AccessDeniedScreenTheme, { config: props.recipe.config, history: history, recipe: props.recipe, + error: props.error, }), } ) @@ -270,6 +285,8 @@ var SessionPreBuiltUI = /** @class */ (function (_super) { children: jsxRuntime.jsx(AccessDeniedScreen$1, { recipe: _this.recipeInstance, useComponentOverrides: useComponentOverrides, + error: props.error, + useShadowDom: props.useShadowDom, }), } ) diff --git a/lib/build/thirdpartyemailpasswordprebuiltui.js b/lib/build/thirdpartyemailpasswordprebuiltui.js index 4a7ef2e2e..b4ef92441 100644 --- a/lib/build/thirdpartyemailpasswordprebuiltui.js +++ b/lib/build/thirdpartyemailpasswordprebuiltui.js @@ -28,8 +28,8 @@ require("supertokens-web-js/utils/sessionClaimValidatorStore"); require("./recipeModule-shared.js"); require("./session-shared2.js"); require("supertokens-web-js/recipe/session"); -require("./session-shared3.js"); require("./session-shared.js"); +require("./session-shared3.js"); require("./emailpassword-shared3.js"); require("./emailverification-shared2.js"); require("./emailpassword-shared7.js"); diff --git a/lib/build/thirdpartypasswordlessprebuiltui.js b/lib/build/thirdpartypasswordlessprebuiltui.js index 84e521e1c..8162a42f8 100644 --- a/lib/build/thirdpartypasswordlessprebuiltui.js +++ b/lib/build/thirdpartypasswordlessprebuiltui.js @@ -28,8 +28,8 @@ require("supertokens-web-js/utils/sessionClaimValidatorStore"); require("./recipeModule-shared.js"); require("./session-shared2.js"); require("supertokens-web-js/recipe/session"); -require("./session-shared3.js"); require("./session-shared.js"); +require("./session-shared3.js"); require("./passwordless-shared.js"); require("supertokens-web-js/utils/error"); require("./emailpassword-shared2.js"); diff --git a/lib/build/thirdpartyprebuiltui.js b/lib/build/thirdpartyprebuiltui.js index f76dfe0fa..15e20a138 100644 --- a/lib/build/thirdpartyprebuiltui.js +++ b/lib/build/thirdpartyprebuiltui.js @@ -24,8 +24,8 @@ require("supertokens-web-js/utils/sessionClaimValidatorStore"); require("./recipeModule-shared.js"); require("./session-shared2.js"); require("supertokens-web-js/recipe/session"); -require("./session-shared3.js"); require("./session-shared.js"); +require("./session-shared3.js"); require("supertokens-web-js/recipe/thirdparty"); require("./authRecipe-shared.js"); require("supertokens-web-js/lib/build/normalisedURLPath"); diff --git a/lib/build/ui-entry.js b/lib/build/ui-entry.js index 5a2e95d9f..af7d68c58 100644 --- a/lib/build/ui-entry.js +++ b/lib/build/ui-entry.js @@ -12,12 +12,12 @@ require("react-dom"); require("./multitenancy-shared.js"); require("./multifactorauth-shared.js"); require("supertokens-web-js/recipe/multifactorauth"); +require("supertokens-web-js/utils"); require("supertokens-web-js/utils/postSuperTokensInitCallbacks"); require("supertokens-web-js/utils/sessionClaimValidatorStore"); require("./recipeModule-shared.js"); require("./session-shared2.js"); require("supertokens-web-js/recipe/session"); -require("supertokens-web-js/utils"); require("supertokens-web-js"); require("supertokens-web-js/utils/cookieHandler"); require("supertokens-web-js/utils/windowHandler"); diff --git a/lib/ts/components/assets/linkIcon.tsx b/lib/ts/components/assets/linkIcon.tsx deleted file mode 100644 index 6815ac02c..000000000 --- a/lib/ts/components/assets/linkIcon.tsx +++ /dev/null @@ -1,12 +0,0 @@ -export const LinkIcon = () => ( - - - - -); diff --git a/lib/ts/recipe/multifactorauth/components/features/factorChooser/index.tsx b/lib/ts/recipe/multifactorauth/components/features/factorChooser/index.tsx index 0f9f51c3e..0d68173a3 100644 --- a/lib/ts/recipe/multifactorauth/components/features/factorChooser/index.tsx +++ b/lib/ts/recipe/multifactorauth/components/features/factorChooser/index.tsx @@ -67,8 +67,15 @@ export const FactorChooser: React.FC = (props) => { mfaInfo.factors.isAllowedToSetup.includes(id) || mfaInfo.factors.isAlreadySetup.includes(id) ) .filter(({ id }) => mfaClaimValue.value!.n.length === 0 || mfaClaimValue.value!.n.includes(id)); - if (availableFactors.length === 1) { - return MultiFactorAuth.getInstanceOrThrow().redirectToFactor(availableFactors[0].id, props.history); + + // If we got here when the next array is not empty, that means that the user redirected here intentionally + // In this case we do not want to automatically redirect away but show the chooser screen. + if (mfaClaimValue.value!.n.length !== 0 && availableFactors.length === 1) { + return MultiFactorAuth.getInstanceOrThrow().redirectToFactor( + availableFactors[0].id, + false, + props.history + ); } else { setMFAInfo(mfaInfo.factors); } @@ -127,7 +134,7 @@ export const FactorChooser: React.FC = (props) => { showBackButton: mfaClaimValue.value?.n.length === 0, mfaInfo: mfaInfo, availableFactors: availableFactors, - logout: signOut, + onLogoutClicked: signOut, navigateToFactor: navigateToFactor, }; diff --git a/lib/ts/recipe/multifactorauth/components/themes/factorChooser/index.tsx b/lib/ts/recipe/multifactorauth/components/themes/factorChooser/index.tsx index 682b89e39..5e8483b52 100644 --- a/lib/ts/recipe/multifactorauth/components/themes/factorChooser/index.tsx +++ b/lib/ts/recipe/multifactorauth/components/themes/factorChooser/index.tsx @@ -15,7 +15,9 @@ import { SuperTokensBranding } from "../../../../../components/SuperTokensBranding"; import { hasFontDefined } from "../../../../../styles/styles"; +import { useTranslation } from "../../../../../translation/translationContext"; import UserContextWrapper from "../../../../../usercontext/userContextWrapper"; +import { AccessDeniedScreen } from "../../../../session/prebuiltui"; import { ThemeBase } from "../themeBase"; import { FactorChooserFooter } from "./factorChooserFooter"; @@ -25,6 +27,15 @@ import { FactorList } from "./factorList"; import type { FactorChooserThemeProps } from "../../../types"; export function FactorChooserTheme(props: FactorChooserThemeProps): JSX.Element { + const t = useTranslation(); + if (props.availableFactors.length === 0) { + return ( + + ); + } return (
- +
); diff --git a/lib/ts/recipe/multifactorauth/components/themes/translations.ts b/lib/ts/recipe/multifactorauth/components/themes/translations.ts index 468d3d52e..f5300cb9f 100644 --- a/lib/ts/recipe/multifactorauth/components/themes/translations.ts +++ b/lib/ts/recipe/multifactorauth/components/themes/translations.ts @@ -5,5 +5,9 @@ export const defaultTranslationsMultiFactorAuth = { ...defaultTranslationsCommon.en, MULTI_FACTOR_CHOOSER_HEADER_TITLE: "Please select a second factor", MULTI_FACTOR_AUTH_LOGOUT: "Logout", + + MFA_NO_AVAILABLE_OPTIONS: "You have no available secondary factors.", + MFA_NO_AVAILABLE_OPTIONS_LOGIN: + "You have no available secondary factors and cannot complete the sign-in process. Please contact support.", }, }; diff --git a/lib/ts/recipe/multifactorauth/index.ts b/lib/ts/recipe/multifactorauth/index.ts index 5161e7145..856a79f39 100644 --- a/lib/ts/recipe/multifactorauth/index.ts +++ b/lib/ts/recipe/multifactorauth/index.ts @@ -46,17 +46,29 @@ export default class Wrapper { }); } + static redirectToFactor(factorId: string, redirectBack = true, history?: any) { + return MultiFactorAuthRecipe.getInstanceOrThrow().redirectToFactor(factorId, redirectBack, history); + } + + static redirectToFactorChooser(redirectBack = true, history?: any) { + return MultiFactorAuthRecipe.getInstanceOrThrow().redirectToFactorChooser(redirectBack, history); + } + static ComponentsOverrideProvider = RecipeComponentsOverrideContextProvider; } const init = Wrapper.init; const getMFAInfo = Wrapper.getMFAInfo; +const redirectToFactor = Wrapper.redirectToFactor; +const redirectToFactorChooser = Wrapper.redirectToFactorChooser; const MultiFactorAuthComponentsOverrideProvider = Wrapper.ComponentsOverrideProvider; const MultiFactorAuthClaim = MultiFactorAuthRecipe.MultiFactorAuthClaim; export { init, getMFAInfo, + redirectToFactor, + redirectToFactorChooser, MultiFactorAuthComponentsOverrideProvider, GetRedirectionURLContext, PreAPIHookContext as PreAPIHookContext, diff --git a/lib/ts/recipe/multifactorauth/recipe.tsx b/lib/ts/recipe/multifactorauth/recipe.tsx index 6d4bcd6ed..82655ee96 100644 --- a/lib/ts/recipe/multifactorauth/recipe.tsx +++ b/lib/ts/recipe/multifactorauth/recipe.tsx @@ -18,12 +18,14 @@ */ import MultiFactorAuthWebJS from "supertokens-web-js/recipe/multifactorauth"; +import { appendQueryParamsToURL } from "supertokens-web-js/utils"; 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 { SSR_ERROR } from "../../constants"; import SuperTokens from "../../superTokens"; +import { getCurrentNormalisedUrlPath, getRedirectToPathFromURL } from "../../utils"; import RecipeModule from "../recipeModule"; import { DEFAULT_FACTOR_CHOOSER_PATH } from "./constants"; @@ -57,8 +59,8 @@ export default class MultiFactorAuth extends RecipeModule< ); public recipeID = MultiFactorAuth.RECIPE_ID; - private readonly firstFactors: string[] = []; - private readonly secondaryFactors: SecondaryFactorRedirectionInfo[] = []; + private readonly firstFactors: Set = new Set(); + private secondaryFactors: SecondaryFactorRedirectionInfo[] = []; constructor( config: NormalisedConfigWithAppInfoAndRecipeID, @@ -130,46 +132,67 @@ export default class MultiFactorAuth extends RecipeModule< getDefaultRedirectionURL = async (context: GetRedirectionURLContext): Promise => { if (context.action === "FACTOR_CHOOSER") { const chooserPath = new NormalisedURLPath(DEFAULT_FACTOR_CHOOSER_PATH); - return `${this.config.appInfo.websiteBasePath.appendPath(chooserPath).getAsStringDangerous()}`; + return this.config.appInfo.websiteBasePath.appendPath(chooserPath).getAsStringDangerous(); } else if (context.action === "GO_TO_FACTOR") { const redirectInfo = this.getSecondaryFactors().find((f) => f.id === context.factorId); if (redirectInfo !== undefined) { - return redirectInfo.path; + return this.config.appInfo.websiteBasePath.appendPath(redirectInfo.path).getAsStringDangerous(); } - // TODO: access denied screen if not defined? - return "/"; + throw new Error("Requested redirect to unknown factor id"); } else { return "/"; } }; - getDefaultFirstFactors(): string[] { - return this.firstFactors; - } - addMFAFactors(firstFactors: string[], secondaryFactors: SecondaryFactorRedirectionInfo[]) { - this.firstFactors.push(...firstFactors); - this.secondaryFactors.push(...secondaryFactors); + for (const fact of firstFactors) { + this.firstFactors.add(fact); + } + this.secondaryFactors = [ + ...this.secondaryFactors.filter((factor) => + secondaryFactors.every((newFactor) => factor.id !== newFactor.id) + ), + ...secondaryFactors, + ]; } getFirstFactors() { - return this.config.firstFactors ?? this.firstFactors; + return this.config.firstFactors ?? Array.from(this.firstFactors); } getSecondaryFactors() { return this.config.getFactorInfo(this.secondaryFactors); } - async redirectToFactor(factorId: string, history?: any) { - return SuperTokens.getInstanceOrThrow().redirectToUrl( - await this.getRedirectUrl({ action: "GO_TO_FACTOR", factorId }), - history - ); + async redirectToFactor(factorId: string, redirectBack = false, history?: any) { + let url = await this.getRedirectUrl({ action: "GO_TO_FACTOR", factorId }); + + if (redirectBack) { + const redirectUrl = getCurrentNormalisedUrlPath().getAsStringDangerous(); + url = appendQueryParamsToURL(url, { redirectToPath: redirectUrl }); + } else { + const redirectUrl = getRedirectToPathFromURL(); + if (redirectUrl) { + url = appendQueryParamsToURL(url, { redirectToPath: redirectUrl }); + } + } + + return SuperTokens.getInstanceOrThrow().redirectToUrl(url, history); } - async redirectToFactorChooser(history?: any) { - return SuperTokens.getInstanceOrThrow().redirectToUrl( - await this.getRedirectUrl({ action: "FACTOR_CHOOSER" }), - history - ); + + async redirectToFactorChooser(redirectBack = false, history?: any) { + let url = await this.getRedirectUrl({ action: "FACTOR_CHOOSER" }); + + if (redirectBack) { + const redirectUrl = getCurrentNormalisedUrlPath().getAsStringDangerous(); + url = appendQueryParamsToURL(url, { redirectToPath: redirectUrl }); + } else { + const redirectUrl = getRedirectToPathFromURL(); + if (redirectUrl) { + url = appendQueryParamsToURL(url, { redirectToPath: redirectUrl }); + } + } + + return SuperTokens.getInstanceOrThrow().redirectToUrl(url, history); } } diff --git a/lib/ts/recipe/multifactorauth/types.ts b/lib/ts/recipe/multifactorauth/types.ts index 1449f6859..08b22e940 100644 --- a/lib/ts/recipe/multifactorauth/types.ts +++ b/lib/ts/recipe/multifactorauth/types.ts @@ -22,6 +22,7 @@ import type { } from "../recipeModule/types"; import type { FC } from "react"; import type { OverrideableBuilder } from "supertokens-js-override"; +import type NormalisedURLPath from "supertokens-web-js/lib/build/normalisedURLPath"; import type { RecipeInterface } from "supertokens-web-js/recipe/multifactorauth"; import type { MFAFactorInfo } from "supertokens-web-js/recipe/multifactorauth/types"; @@ -91,7 +92,7 @@ export type FactorChooserThemeProps = { showBackButton: boolean; onBackButtonClicked: () => void; navigateToFactor: (factorId: string) => void; - logout: () => void; + onLogoutClicked: () => void; config: NormalisedConfig; userContext?: any; }; @@ -101,5 +102,5 @@ export type SecondaryFactorRedirectionInfo = { name: string; description: string; logo: FC; - path: string; + path: NormalisedURLPath; }; diff --git a/lib/ts/recipe/passwordless/recipe.tsx b/lib/ts/recipe/passwordless/recipe.tsx index c070bd1ad..2348937f6 100644 --- a/lib/ts/recipe/passwordless/recipe.tsx +++ b/lib/ts/recipe/passwordless/recipe.tsx @@ -18,9 +18,9 @@ */ import PasswordlessWebJS from "supertokens-web-js/recipe/passwordless"; +import NormalisedURLPath from "supertokens-web-js/utils/normalisedURLPath"; import { PostSuperTokensInitCallbacks } from "supertokens-web-js/utils/postSuperTokensInitCallbacks"; -import { LinkIcon } from "../../components/assets/linkIcon"; import { OTPIcon } from "../../components/assets/otpIcon"; import { SSR_ERROR } from "../../constants"; import { isTest } from "../../utils"; @@ -81,31 +81,16 @@ export default class Passwordless extends AuthRecipe< id: "otp-phone", name: "SMS based OTP", description: "Get an OTP code on your phone to complete the authentication request", - path: "/check-auth/otp-phone", + path: new NormalisedURLPath("/check-auth/otp-phone"), logo: OTPIcon, }, - { - id: "link-phone", - name: "SMS based Magic link", - description: "Get a magic link on your phone to complete the authentication request", - path: "/check-auth/link-phone", - logo: LinkIcon, - }, { id: "otp-email", name: "SMS based OTP", description: "Get an OTP code on your email address to complete the authentication request", - path: "/check-auth/otp-email", + path: new NormalisedURLPath("/check-auth/otp-email"), logo: OTPIcon, }, - { - id: "link-email", - name: "SMS based Magic link", - description: - "Get a magic link on your email address to complete the authentication request", - path: "/check-auth/link-email", - logo: LinkIcon, - }, ] ); } diff --git a/lib/ts/recipe/recipeRouter/index.tsx b/lib/ts/recipe/recipeRouter/index.tsx index c6d43c30c..9ece4880b 100644 --- a/lib/ts/recipe/recipeRouter/index.tsx +++ b/lib/ts/recipe/recipeRouter/index.tsx @@ -8,6 +8,7 @@ import type RecipeModule from "../recipeModule"; import type NormalisedURLPath from "supertokens-web-js/lib/build/normalisedURLPath"; // The related ADR: https://supertokens.com/docs/contribute/decisions/multitenancy/0006 +// TODO: This could be by the recipes registering what factors they provide (at least partially) const priorityOrder: { rid: string; includes: ("thirdparty" | "emailpassword" | "passwordless")[]; @@ -52,9 +53,9 @@ function chooseComponentBasedOnFirstFactors( let maxProvided = 0; let component = undefined; // We find the component that provides the most factors - for (const { rid, factorsProvided } of priorityOrder.reverse()) { + for (const { rid, factorsProvided } of priorityOrder) { const providedByCurrent = factorsProvided.filter((id) => firstFactors.includes(id)).length; - if (providedByCurrent >= maxProvided) { + if (providedByCurrent > maxProvided) { const matchingComp = routeComponents.find((comp) => comp.recipeID === rid); if (matchingComp) { maxProvided = providedByCurrent; @@ -63,6 +64,10 @@ function chooseComponentBasedOnFirstFactors( } } + if (component === undefined) { + throw new Error("No enabled recipes overlap with the requested firstFactors: " + firstFactors); + } + return component; } diff --git a/lib/ts/recipe/session/components/features/accessDeniedScreen/index.tsx b/lib/ts/recipe/session/components/features/accessDeniedScreen/index.tsx index 3da527c3c..d7a66ff73 100644 --- a/lib/ts/recipe/session/components/features/accessDeniedScreen/index.tsx +++ b/lib/ts/recipe/session/components/features/accessDeniedScreen/index.tsx @@ -14,6 +14,8 @@ const AccessDeniedScreen: React.FC< FeatureBaseProps & { recipe: Recipe; useComponentOverrides: () => ComponentOverrideMap; + useShadowDom?: boolean; + error?: string; } > = (props) => { const recipeComponentOverrides = props.useComponentOverrides(); @@ -21,8 +23,15 @@ const AccessDeniedScreen: React.FC< return ( - - + + ); diff --git a/lib/ts/recipe/session/components/themes/accessDeniedScreenTheme/index.tsx b/lib/ts/recipe/session/components/themes/accessDeniedScreenTheme/index.tsx index 13d904618..fa3ceb5e3 100644 --- a/lib/ts/recipe/session/components/themes/accessDeniedScreenTheme/index.tsx +++ b/lib/ts/recipe/session/components/themes/accessDeniedScreenTheme/index.tsx @@ -47,6 +47,7 @@ const AccessDeniedScreen: FC = (props) => {
{t("ACCESS_DENIED")}
+ {props.error &&
{props.error}
}
diff --git a/lib/ts/recipe/session/components/themes/styles.css b/lib/ts/recipe/session/components/themes/styles.css index 053a63e9a..624b6a843 100644 --- a/lib/ts/recipe/session/components/themes/styles.css +++ b/lib/ts/recipe/session/components/themes/styles.css @@ -45,6 +45,7 @@ } [data-supertokens~="buttonsGroup"] { + margin-top: 24px; display: flex; align-items: center; justify-content: space-between; diff --git a/lib/ts/recipe/session/prebuiltui.tsx b/lib/ts/recipe/session/prebuiltui.tsx index 3adff1e5a..217cafa7f 100644 --- a/lib/ts/recipe/session/prebuiltui.tsx +++ b/lib/ts/recipe/session/prebuiltui.tsx @@ -33,7 +33,7 @@ export class SessionPreBuiltUI extends RecipeRouter { } static getFeatureComponent( componentName: "accessDenied", - props: FeatureBaseProps & { redirectOnSessionExists?: boolean; userContext?: any }, + props: FeatureBaseProps & { error?: string; userContext?: any }, useComponentOverrides: () => GenericComponentOverrideMap = useRecipeComponentOverrideContext ): JSX.Element { return SessionPreBuiltUI.getInstanceOrInitAndGetInstance().getFeatureComponent( @@ -52,7 +52,7 @@ export class SessionPreBuiltUI extends RecipeRouter { }; getFeatureComponent = ( componentName: "accessDenied", - props: FeatureBaseProps & { userContext?: any }, + props: FeatureBaseProps & { useShadowDom?: boolean; error?: string; userContext?: any }, useComponentOverrides: () => GenericComponentOverrideMap = useRecipeComponentOverrideContext ): JSX.Element => { if (componentName === "accessDenied") { @@ -61,6 +61,8 @@ export class SessionPreBuiltUI extends RecipeRouter { ); @@ -78,8 +80,9 @@ export class SessionPreBuiltUI extends RecipeRouter { return; } - static AccessDeniedScreen = (prop: PropsWithChildren<{ userContext?: any }> = {}): ReactElement => - this.getFeatureComponent("accessDenied", prop); + static AccessDeniedScreen = ( + prop: PropsWithChildren<{ useShadowDom?: boolean; error?: string; userContext?: any }> = {} + ): ReactElement => this.getFeatureComponent("accessDenied", prop); static AccessDeniedScreenTheme = AccessDeniedScreenTheme; } diff --git a/lib/ts/recipe/session/types.ts b/lib/ts/recipe/session/types.ts index 6aae78bc3..92c7bdfa6 100644 --- a/lib/ts/recipe/session/types.ts +++ b/lib/ts/recipe/session/types.ts @@ -26,12 +26,14 @@ import type { UserInput as WebJSInputType, RecipeEvent } from "supertokens-web-j export type RecipeEventWithSessionContext = RecipeEvent & { sessionContext: SessionContextUpdate }; export type InputType = WebJSInputType & { + useShadowDom?: boolean; style?: string; accessDeniedScreen?: SessionFeatureBaseConfig; onHandleEvent?: (event: RecipeEventWithSessionContext) => void; }; export type NormalisedSessionConfig = NormalisedConfig & { + useShadowDom: boolean; accessDeniedScreen: NormalisedBaseConfig; override: { functions: ( @@ -64,6 +66,7 @@ export type SessionContextType = export type AccessDeniedThemeProps = { recipe: Session; history: any; + error?: string; config: NormalisedSessionConfig; }; From fa3d6d5a4a5642123aa1fd87bfeabde6f27520a0 Mon Sep 17 00:00:00 2001 From: Mihaly Lengyel Date: Wed, 1 Nov 2023 00:28:55 +0100 Subject: [PATCH 2/2] feat: self-review fixes&cleanup --- lib/build/passwordless-shared2.js | 42 ++-- lib/build/passwordless-shared3.js | 233 +++++++++++------- lib/build/passwordlessprebuiltui.js | 3 +- .../components/themes/mfa/index.d.ts | 1 + .../components/themes/translations.d.ts | 1 + lib/build/recipe/passwordless/recipe.d.ts | 16 ++ .../components/themes/translations.d.ts | 1 + lib/build/thirdpartypasswordlessprebuiltui.js | 5 +- .../components/features/mfa/index.tsx | 20 +- .../components/themes/mfa/index.tsx | 143 ++++++----- .../components/themes/mfa/mfaFooter.tsx | 4 +- .../components/themes/mfa/mfaOTPFooter.tsx | 30 ++- .../passwordless/components/themes/styles.css | 25 ++ .../components/themes/translations.ts | 2 + lib/ts/recipe/passwordless/recipe.tsx | 53 ++-- 15 files changed, 360 insertions(+), 219 deletions(-) diff --git a/lib/build/passwordless-shared2.js b/lib/build/passwordless-shared2.js index 65ce53dd3..b694a3096 100644 --- a/lib/build/passwordless-shared2.js +++ b/lib/build/passwordless-shared2.js @@ -526,9 +526,20 @@ function normalisePasswordlessBaseConfig(config) { * License for the specific language governing permissions and limitations * under the License. */ -/* - * Class. - */ +var otpPhoneFactor = { + id: "otp-phone", + name: "SMS based OTP", + description: "Get an OTP code on your phone to complete the authentication request", + path: new NormalisedURLPath__default.default("/mfa/otp-phone"), + logo: OTPIcon, +}; +var otpEmailFactor = { + id: "otp-email", + name: "Email based OTP", + description: "Get an OTP code on your email address to complete the authentication request", + path: new NormalisedURLPath__default.default("/mfa/otp-email"), + logo: OTPIcon, +}; var Passwordless = /** @class */ (function (_super) { genericComponentOverrideContext.__extends(Passwordless, _super); function Passwordless(config, webJSRecipe) { @@ -545,34 +556,19 @@ var Passwordless = /** @class */ (function (_super) { }); }); }; - return _this; - } - 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: "SMS based OTP", - description: "Get an OTP code on your phone to complete the authentication request", - path: new NormalisedURLPath__default.default("/check-auth/otp-phone"), - logo: OTPIcon, - }, - { - id: "otp-email", - name: "Email based OTP", - description: "Get an OTP code on your email address to complete the authentication request", - path: new NormalisedURLPath__default.default("/check-auth/otp-email"), - logo: OTPIcon, - }, - ] + [otpPhoneFactor, otpEmailFactor] ); } }); + return _this; + } + Passwordless.init = function (config) { + var normalisedConfig = normalisePasswordlessConfig(config); return { recipeID: Passwordless.RECIPE_ID, authReact: function (appInfo) { diff --git a/lib/build/passwordless-shared3.js b/lib/build/passwordless-shared3.js index 5b574b913..ceecf13f8 100644 --- a/lib/build/passwordless-shared3.js +++ b/lib/build/passwordless-shared3.js @@ -18,6 +18,7 @@ var recipe$2 = require("./multifactorauth-shared.js"); var recipe$1 = require("./passwordless-shared2.js"); var SuperTokensBranding = require("./SuperTokensBranding.js"); var generalError = require("./emailpassword-shared.js"); +var sessionprebuiltui = require("./sessionprebuiltui.js"); var checkedRoundIcon = require("./checkedRoundIcon.js"); var formBase = require("./emailpassword-shared8.js"); var validators = require("./emailpassword-shared5.js"); @@ -60,7 +61,7 @@ var React__namespace = /*#__PURE__*/ _interopNamespace(React); var STGeneralError__default = /*#__PURE__*/ _interopDefault(STGeneralError); var styles = - '/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.\n *\n * This software is licensed under the Apache License, Version 2.0 (the\n * "License") as published by the Apache Software Foundation.\n *\n * You may not use this file except in compliance with the License. You may\n * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations\n * under the License.\n */\n\n[data-supertokens~="container"] {\n --palette-background: 255, 255, 255;\n --palette-inputBackground: 250, 250, 250;\n --palette-inputBorder: 224, 224, 224;\n --palette-primary: 255, 155, 51;\n --palette-primaryBorder: 238, 141, 35;\n --palette-success: 65, 167, 0;\n --palette-successBackground: 217, 255, 191;\n --palette-error: 255, 23, 23;\n --palette-errorBackground: 255, 241, 235;\n --palette-textTitle: 34, 34, 34;\n --palette-textLabel: 34, 34, 34;\n --palette-textInput: 34, 34, 34;\n --palette-textPrimary: 101, 101, 101;\n --palette-textLink: 0, 118, 255;\n --palette-buttonText: 255, 255, 255;\n --palette-textGray: 128, 128, 128;\n --palette-superTokensBrandingBackground: 242, 245, 246;\n --palette-superTokensBrandingText: 173, 189, 196;\n\n --font-size-0: 12px;\n --font-size-1: 14px;\n --font-size-2: 16px;\n --font-size-3: 19px;\n --font-size-4: 24px;\n}\n\n/*\n * Default styles.\n */\n\n@-webkit-keyframes slideTop {\n 0% {\n -webkit-transform: translateY(-5px);\n transform: translateY(-5px);\n }\n 100% {\n -webkit-transform: translateY(0px);\n transform: translateY(0px);\n }\n}\n\n@keyframes slideTop {\n 0% {\n -webkit-transform: translateY(-5px);\n transform: translateY(-5px);\n }\n 100% {\n -webkit-transform: translateY(0px);\n transform: translateY(0px);\n }\n}\n\n@-webkit-keyframes swing-in-top-fwd {\n 0% {\n -webkit-transform: rotateX(-100deg);\n transform: rotateX(-100deg);\n -webkit-transform-origin: top;\n transform-origin: top;\n opacity: 0;\n }\n 100% {\n -webkit-transform: rotateX(0deg);\n transform: rotateX(0deg);\n -webkit-transform-origin: top;\n transform-origin: top;\n opacity: 1;\n }\n}\n\n@keyframes swing-in-top-fwd {\n 0% {\n -webkit-transform: rotateX(-100deg);\n transform: rotateX(-100deg);\n -webkit-transform-origin: top;\n transform-origin: top;\n opacity: 0;\n }\n 100% {\n -webkit-transform: rotateX(0deg);\n transform: rotateX(0deg);\n -webkit-transform-origin: top;\n transform-origin: top;\n opacity: 1;\n }\n}\n\n[data-supertokens~="container"] {\n font-family: "Rubik", sans-serif;\n margin: 12px auto;\n margin-top: 26px;\n margin-bottom: 26px;\n width: 420px;\n text-align: center;\n border-radius: 8px;\n box-shadow: 1px 1px 10px rgba(0, 0, 0, 0.16);\n background-color: rgb(var(--palette-background));\n}\n\n@media (max-width: 440px) {\n [data-supertokens~="container"] {\n width: 95vw;\n }\n}\n\n[data-supertokens~="row"] {\n margin: 0 auto;\n width: 76%;\n padding-top: 30px;\n padding-bottom: 10px;\n}\n\n[data-supertokens~="superTokensBranding"] {\n display: block;\n margin: 0 auto;\n background: rgb(var(--palette-superTokensBrandingBackground));\n color: rgb(var(--palette-superTokensBrandingText));\n text-decoration: none;\n width: -webkit-fit-content;\n width: -moz-fit-content;\n width: fit-content;\n border-radius: 6px 6px 0 0;\n padding: 4px 9px;\n font-weight: 300;\n font-size: var(--font-size-0);\n letter-spacing: 0.4px;\n}\n\n[data-supertokens~="generalError"] {\n background: rgb(var(--palette-errorBackground));\n padding-top: 10px;\n padding-bottom: 10px;\n margin-bottom: 15px;\n padding-left: 18px;\n padding-right: 18px;\n letter-spacing: 0.2px;\n font-size: var(--font-size-1);\n border-radius: 8px;\n color: rgb(var(--palette-error));\n -webkit-animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;\n animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;\n word-wrap: break-word;\n}\n\n[data-supertokens~="headerTitle"] {\n font-size: var(--font-size-4);\n line-height: 40px;\n letter-spacing: 0.58px;\n font-weight: 800;\n margin-bottom: 2px;\n color: rgb(var(--palette-textTitle));\n}\n\n[data-supertokens~="headerSubtitle"] {\n margin-bottom: 21px;\n}\n\n[data-supertokens~="privacyPolicyAndTermsAndConditions"] {\n max-width: 300px;\n margin-top: 10px;\n}\n\n[data-supertokens~="privacyPolicyAndTermsAndConditions"] a {\n line-height: 21px;\n}\n\n/* TODO: split the link style into separate things*/\n\n/* We add this before primary and secondary text, because if they are applied to the same element the other ones take priority */\n\n[data-supertokens~="link"] {\n padding-left: 3px;\n padding-right: 3px;\n color: rgb(var(--palette-textLink));\n font-size: var(--font-size-1);\n cursor: pointer;\n letter-spacing: 0.16px;\n line-height: 26px;\n}\n\n[data-supertokens~="primaryText"] {\n font-size: var(--font-size-1);\n font-weight: 500;\n letter-spacing: 0.4px;\n line-height: 21px;\n color: rgb(var(--palette-textLabel));\n}\n\n[data-supertokens~="secondaryText"] {\n font-size: var(--font-size-1);\n font-weight: 300;\n letter-spacing: 0.4px;\n color: rgb(var(--palette-textPrimary));\n}\n\n[data-supertokens~="divider"] {\n margin-top: 1em;\n margin-bottom: 1em;\n border-bottom: 0.3px solid #dddddd;\n align-items: center;\n padding-bottom: 5px;\n}\n\n[data-supertokens~="headerTinyTitle"] {\n margin-top: 13px;\n font-size: var(--font-size-3);\n letter-spacing: 1.1px;\n font-weight: 500;\n line-height: 28px;\n}\n\n[data-supertokens~="secondaryLinkWithArrow"] {\n margin-top: 10px;\n margin-bottom: 30px;\n cursor: pointer;\n}\n\n[data-supertokens~="secondaryLinkWithArrow"]:hover {\n position: relative;\n left: 2px;\n word-spacing: 4px;\n}\n\n[data-supertokens~="generalSuccess"] {\n color: rgb(var(--palette-success));\n font-size: var(--font-size-1);\n background: rgb(var(--palette-successBackground));\n -webkit-animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;\n animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;\n padding: 9px 15px 9px 15px;\n border-radius: 6px;\n display: inline-block;\n}\n\n[data-supertokens~="spinner"] {\n width: 80px;\n height: auto;\n padding-top: 20px;\n padding-bottom: 40px;\n margin: 0 auto;\n}\n\n[data-supertokens~="error"] {\n color: rgb(var(--palette-error));\n}\n\n[data-supertokens~="linkButton"] {\n background-color: transparent;\n border: 0;\n}\n\n[data-supertokens~="secondaryLinkWithLeftArrow"] {\n margin-top: 10px;\n margin-bottom: 40px;\n cursor: pointer;\n}\n\n[data-supertokens~="secondaryLinkWithLeftArrow"] svg {\n margin-right: 0.3em;\n}\n\n[data-supertokens~="secondaryLinkWithLeftArrow"]:hover svg {\n position: relative;\n left: -4px;\n}\n\n[data-supertokens~="button"] {\n background-color: rgb(var(--palette-primary));\n color: rgb(var(--palette-buttonText));\n width: 100%;\n height: 34px;\n font-weight: 700;\n border-width: 1px;\n border-style: solid;\n border-radius: 6px;\n border-color: rgb(var(--palette-primaryBorder));\n background-position: center;\n transition: all 0.4s;\n background-size: 12000%;\n cursor: pointer;\n}\n\n[data-supertokens~="button"]:disabled {\n border: none;\n cursor: no-drop;\n}\n\n[data-supertokens~="button"]:active {\n outline: none;\n transition: all 0s;\n background-size: 100%;\n -webkit-filter: brightness(0.85);\n filter: brightness(0.85);\n}\n\n[data-supertokens~="button"]:focus {\n outline: none;\n}\n\n[data-supertokens~="backButtonCommon"] {\n width: 16px;\n height: 13px;\n}\n\n[data-supertokens~="backButton"] {\n cursor: pointer;\n border: none;\n background-color: transparent;\n padding: 0px;\n}\n\n[data-supertokens~="backButtonPlaceholder"] {\n display: block;\n}\n\n/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.\n *\n * This software is licensed under the Apache License, Version 2.0 (the\n * "License") as published by the Apache Software Foundation.\n *\n * You may not use this file except in compliance with the License. You may\n * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations\n * under the License.\n */\n\n[data-supertokens~="inputContainer"] {\n margin-top: 6px;\n}\n\n[data-supertokens~="inputWrapper"] {\n box-sizing: border-box;\n width: 100%;\n display: flex;\n align-items: center;\n background-color: rgb(var(--palette-inputBackground));\n height: 34px;\n border-radius: 6px;\n border: 1px solid rgb(var(--palette-inputBorder));\n}\n\n[data-supertokens~="inputWrapper"][focus-within] {\n background-color: rgba(var(--palette-inputBackground), 0.25);\n border: 1px solid rgb(var(--palette-primary));\n box-shadow: 0 0 0 0.2rem rgba(var(--palette-primary), 0.25);\n outline: none;\n}\n\n[data-supertokens~="inputWrapper"]:focus-within {\n background-color: rgba(var(--palette-inputBackground), 0.25);\n border: 1px solid rgb(var(--palette-primary));\n box-shadow: 0 0 0 0.2rem rgba(var(--palette-primary), 0.25);\n outline: none;\n}\n\n[data-supertokens~="inputError"] {\n border: 1px solid rgb(var(--palette-error));\n box-shadow: 0 0 0 0.2rem rgba(var(--palette-error), 0.25);\n outline: none;\n}\n\n[data-supertokens~="inputError"][focus-within] {\n border: 1px solid rgb(var(--palette-error));\n box-shadow: 0 0 0 0.2rem rgba(var(--palette-error), 0.25);\n outline: none;\n}\n\n[data-supertokens~="inputError"]:focus-within {\n border: 1px solid rgb(var(--palette-error));\n box-shadow: 0 0 0 0.2rem rgba(var(--palette-error), 0.25);\n outline: none;\n}\n\n[data-supertokens~="input"] {\n box-sizing: border-box;\n padding-left: 15px;\n -webkit-filter: none;\n filter: none;\n color: rgb(var(--palette-textInput));\n background-color: transparent;\n border-radius: 6px;\n font-size: var(--font-size-1);\n border: none;\n padding-right: 25px;\n letter-spacing: 1.2px;\n flex: 9 1 75%;\n width: 75%;\n height: 32px;\n}\n\n[data-supertokens~="input"]:focus {\n border: none;\n outline: none;\n}\n\n[data-supertokens~="input"]:-webkit-autofill,\n[data-supertokens~="input"]:-webkit-autofill:hover,\n[data-supertokens~="input"]:-webkit-autofill:focus,\n[data-supertokens~="input"]:-webkit-autofill:active {\n -webkit-text-fill-color: rgb(var(--palette-textInput));\n box-shadow: 0 0 0 30px rgb(var(--palette-inputBackground)) inset;\n}\n\n[data-supertokens~="inputAdornment"] {\n justify-content: center;\n margin-right: 5px;\n}\n\n[data-supertokens~="showPassword"] {\n cursor: pointer;\n}\n\n[data-supertokens~="forgotPasswordLink"] {\n margin-top: 10px;\n}\n\n[data-supertokens~="enterEmailSuccessMessage"] {\n margin-top: 15px;\n margin-bottom: 15px;\n word-break: break-word;\n}\n\n[data-supertokens~="submitNewPasswordSuccessMessage"] {\n margin-top: 15px;\n margin-bottom: 15px;\n}\n\n[data-supertokens~="inputErrorMessage"] {\n padding-top: 5px;\n padding-bottom: 5px;\n color: rgb(var(--palette-error));\n line-height: 24px;\n font-weight: 400;\n font-size: var(--font-size-1);\n text-align: left;\n -webkit-animation: slideTop 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;\n animation: slideTop 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;\n max-width: 330px;\n}\n\n@media (max-width: 440px) {\n [data-supertokens~="inputErrorMessage"] {\n max-width: 250px;\n }\n}\n\n[data-supertokens~="inputErrorSymbol"] {\n margin-right: 5px;\n top: 1px;\n position: relative;\n left: 2px;\n}\n\n[data-supertokens~="label"] {\n text-align: left;\n font-weight: 600;\n font-size: var(--font-size-1);\n line-height: 24px;\n color: rgb(var(--palette-textLabel));\n}\n\n[data-supertokens~="formRow"] {\n display: flex;\n flex-direction: column;\n padding-top: 0px;\n padding-bottom: 34px;\n}\n\n[data-supertokens~="formRow"][data-supertokens~="hasError"] {\n padding-bottom: 0;\n}\n\n[data-supertokens~="sendVerifyEmailIcon"] {\n margin-top: 11px;\n}\n\n[data-supertokens~="headerTinyTitle"] {\n margin-top: 13px;\n font-size: var(--font-size-3);\n letter-spacing: 1.1px;\n font-weight: 500;\n line-height: 28px;\n}\n\n[data-supertokens~="sendVerifyEmailText"] {\n line-height: 21px;\n font-size: var(--font-size-1);\n text-align: center;\n font-weight: 300;\n letter-spacing: 0.8px;\n}\n\n[data-supertokens~="secondaryLinkWithArrow"] {\n margin-top: 10px;\n margin-bottom: 30px;\n cursor: pointer;\n}\n\n[data-supertokens~="secondaryLinkWithArrow"]:hover {\n position: relative;\n left: 2px;\n word-spacing: 4px;\n}\n\n[data-supertokens~="sendVerifyEmailResend"] {\n margin-top: 13px;\n font-weight: 300;\n}\n\n[data-supertokens~="sendVerifyEmailResend"]:hover {\n text-decoration: underline;\n}\n\n[data-supertokens~="noFormRow"] {\n padding-bottom: 25px;\n}\n\n[data-supertokens~="emailVerificationButtonWrapper"] {\n padding-top: 25px;\n max-width: 96px;\n margin: 0 auto;\n}\n\n[data-supertokens~="withBackButton"] {\n position: relative;\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n[data-supertokens~="resendEmailLink"] {\n display: inline-block;\n}\n\n[data-supertokens~="generalSuccess"] {\n margin-bottom: 20px;\n -webkit-animation: swingIn 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) alternate 2 both;\n animation: swingIn 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) alternate 2 both;\n}\n\n[data-supertokens~="codeInputLabelWrapper"] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n[data-supertokens~="headerSubtitle"] strong {\n max-width: 100%;\n display: inline-block;\n vertical-align: bottom;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n[data-supertokens~="sendCodeText"] {\n margin-top: 15px;\n margin-bottom: 20px;\n}\n\n[data-supertokens~="sendCodeText"] strong {\n max-width: 100%;\n display: inline-block;\n vertical-align: bottom;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n[data-supertokens~="resendCodeBtn"] {\n width: auto;\n margin-top: 0;\n line-height: 24px;\n}\n\n[data-supertokens~="resendCodeBtn"]:hover {\n text-decoration: underline;\n}\n\n[data-supertokens~="resendCodeBtn"]:disabled {\n color: rgb(var(--palette-textPrimary));\n cursor: default;\n text-decoration: none;\n}\n\n[data-supertokens~="phoneInputLibRoot"] {\n display: flex;\n align-items: center;\n}\n\n[data-supertokens~="phoneInputWrapper"] {\n display: flex;\n align-items: center;\n}\n\n[data-supertokens~="phoneInputWrapper"] .iti [data-supertokens~="input"] {\n padding-left: 15px;\n}\n\n[data-supertokens~="phoneInputWrapper"] .iti {\n flex: 1 1;\n min-width: 0;\n width: 100%;\n background: transparent;\n border: none;\n color: inherit;\n outline: none;\n}\n\n[data-supertokens~="continueButtonWrapper"] {\n margin-top: 10px;\n margin-bottom: 30px;\n}\n\n.iti__country-list {\n border: 0;\n top: 40px;\n width: min(72.2vw, 320px);\n border-radius: 6;\n box-shadow: 0px 0px 3px 0px rgba(0, 0, 0, 0.16);\n}\n\n.iti__country {\n display: flex;\n align-items: center;\n height: 34px;\n cursor: pointer;\n\n padding: 0 8px;\n}\n\n.iti__country-name {\n color: var(--palette-textLabel);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n margin: "0 16px";\n}\n'; + '/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.\n *\n * This software is licensed under the Apache License, Version 2.0 (the\n * "License") as published by the Apache Software Foundation.\n *\n * You may not use this file except in compliance with the License. You may\n * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations\n * under the License.\n */\n\n[data-supertokens~="container"] {\n --palette-background: 255, 255, 255;\n --palette-inputBackground: 250, 250, 250;\n --palette-inputBorder: 224, 224, 224;\n --palette-primary: 255, 155, 51;\n --palette-primaryBorder: 238, 141, 35;\n --palette-success: 65, 167, 0;\n --palette-successBackground: 217, 255, 191;\n --palette-error: 255, 23, 23;\n --palette-errorBackground: 255, 241, 235;\n --palette-textTitle: 34, 34, 34;\n --palette-textLabel: 34, 34, 34;\n --palette-textInput: 34, 34, 34;\n --palette-textPrimary: 101, 101, 101;\n --palette-textLink: 0, 118, 255;\n --palette-buttonText: 255, 255, 255;\n --palette-textGray: 128, 128, 128;\n --palette-superTokensBrandingBackground: 242, 245, 246;\n --palette-superTokensBrandingText: 173, 189, 196;\n\n --font-size-0: 12px;\n --font-size-1: 14px;\n --font-size-2: 16px;\n --font-size-3: 19px;\n --font-size-4: 24px;\n}\n\n/*\n * Default styles.\n */\n\n@-webkit-keyframes slideTop {\n 0% {\n -webkit-transform: translateY(-5px);\n transform: translateY(-5px);\n }\n 100% {\n -webkit-transform: translateY(0px);\n transform: translateY(0px);\n }\n}\n\n@keyframes slideTop {\n 0% {\n -webkit-transform: translateY(-5px);\n transform: translateY(-5px);\n }\n 100% {\n -webkit-transform: translateY(0px);\n transform: translateY(0px);\n }\n}\n\n@-webkit-keyframes swing-in-top-fwd {\n 0% {\n -webkit-transform: rotateX(-100deg);\n transform: rotateX(-100deg);\n -webkit-transform-origin: top;\n transform-origin: top;\n opacity: 0;\n }\n 100% {\n -webkit-transform: rotateX(0deg);\n transform: rotateX(0deg);\n -webkit-transform-origin: top;\n transform-origin: top;\n opacity: 1;\n }\n}\n\n@keyframes swing-in-top-fwd {\n 0% {\n -webkit-transform: rotateX(-100deg);\n transform: rotateX(-100deg);\n -webkit-transform-origin: top;\n transform-origin: top;\n opacity: 0;\n }\n 100% {\n -webkit-transform: rotateX(0deg);\n transform: rotateX(0deg);\n -webkit-transform-origin: top;\n transform-origin: top;\n opacity: 1;\n }\n}\n\n[data-supertokens~="container"] {\n font-family: "Rubik", sans-serif;\n margin: 12px auto;\n margin-top: 26px;\n margin-bottom: 26px;\n width: 420px;\n text-align: center;\n border-radius: 8px;\n box-shadow: 1px 1px 10px rgba(0, 0, 0, 0.16);\n background-color: rgb(var(--palette-background));\n}\n\n@media (max-width: 440px) {\n [data-supertokens~="container"] {\n width: 95vw;\n }\n}\n\n[data-supertokens~="row"] {\n margin: 0 auto;\n width: 76%;\n padding-top: 30px;\n padding-bottom: 10px;\n}\n\n[data-supertokens~="superTokensBranding"] {\n display: block;\n margin: 0 auto;\n background: rgb(var(--palette-superTokensBrandingBackground));\n color: rgb(var(--palette-superTokensBrandingText));\n text-decoration: none;\n width: -webkit-fit-content;\n width: -moz-fit-content;\n width: fit-content;\n border-radius: 6px 6px 0 0;\n padding: 4px 9px;\n font-weight: 300;\n font-size: var(--font-size-0);\n letter-spacing: 0.4px;\n}\n\n[data-supertokens~="generalError"] {\n background: rgb(var(--palette-errorBackground));\n padding-top: 10px;\n padding-bottom: 10px;\n margin-bottom: 15px;\n padding-left: 18px;\n padding-right: 18px;\n letter-spacing: 0.2px;\n font-size: var(--font-size-1);\n border-radius: 8px;\n color: rgb(var(--palette-error));\n -webkit-animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;\n animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;\n word-wrap: break-word;\n}\n\n[data-supertokens~="headerTitle"] {\n font-size: var(--font-size-4);\n line-height: 40px;\n letter-spacing: 0.58px;\n font-weight: 800;\n margin-bottom: 2px;\n color: rgb(var(--palette-textTitle));\n}\n\n[data-supertokens~="headerSubtitle"] {\n margin-bottom: 21px;\n}\n\n[data-supertokens~="privacyPolicyAndTermsAndConditions"] {\n max-width: 300px;\n margin-top: 10px;\n}\n\n[data-supertokens~="privacyPolicyAndTermsAndConditions"] a {\n line-height: 21px;\n}\n\n/* TODO: split the link style into separate things*/\n\n/* We add this before primary and secondary text, because if they are applied to the same element the other ones take priority */\n\n[data-supertokens~="link"] {\n padding-left: 3px;\n padding-right: 3px;\n color: rgb(var(--palette-textLink));\n font-size: var(--font-size-1);\n cursor: pointer;\n letter-spacing: 0.16px;\n line-height: 26px;\n}\n\n[data-supertokens~="primaryText"] {\n font-size: var(--font-size-1);\n font-weight: 500;\n letter-spacing: 0.4px;\n line-height: 21px;\n color: rgb(var(--palette-textLabel));\n}\n\n[data-supertokens~="secondaryText"] {\n font-size: var(--font-size-1);\n font-weight: 300;\n letter-spacing: 0.4px;\n color: rgb(var(--palette-textPrimary));\n}\n\n[data-supertokens~="divider"] {\n margin-top: 1em;\n margin-bottom: 1em;\n border-bottom: 0.3px solid #dddddd;\n align-items: center;\n padding-bottom: 5px;\n}\n\n[data-supertokens~="headerTinyTitle"] {\n margin-top: 13px;\n font-size: var(--font-size-3);\n letter-spacing: 1.1px;\n font-weight: 500;\n line-height: 28px;\n}\n\n[data-supertokens~="secondaryLinkWithArrow"] {\n margin-top: 10px;\n margin-bottom: 30px;\n cursor: pointer;\n}\n\n[data-supertokens~="secondaryLinkWithArrow"]:hover {\n position: relative;\n left: 2px;\n word-spacing: 4px;\n}\n\n[data-supertokens~="generalSuccess"] {\n color: rgb(var(--palette-success));\n font-size: var(--font-size-1);\n background: rgb(var(--palette-successBackground));\n -webkit-animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;\n animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;\n padding: 9px 15px 9px 15px;\n border-radius: 6px;\n display: inline-block;\n}\n\n[data-supertokens~="spinner"] {\n width: 80px;\n height: auto;\n padding-top: 20px;\n padding-bottom: 40px;\n margin: 0 auto;\n}\n\n[data-supertokens~="error"] {\n color: rgb(var(--palette-error));\n}\n\n[data-supertokens~="linkButton"] {\n background-color: transparent;\n border: 0;\n}\n\n[data-supertokens~="secondaryLinkWithLeftArrow"] {\n margin-top: 10px;\n margin-bottom: 40px;\n cursor: pointer;\n}\n\n[data-supertokens~="secondaryLinkWithLeftArrow"] svg {\n margin-right: 0.3em;\n}\n\n[data-supertokens~="secondaryLinkWithLeftArrow"]:hover svg {\n position: relative;\n left: -4px;\n}\n\n[data-supertokens~="button"] {\n background-color: rgb(var(--palette-primary));\n color: rgb(var(--palette-buttonText));\n width: 100%;\n height: 34px;\n font-weight: 700;\n border-width: 1px;\n border-style: solid;\n border-radius: 6px;\n border-color: rgb(var(--palette-primaryBorder));\n background-position: center;\n transition: all 0.4s;\n background-size: 12000%;\n cursor: pointer;\n}\n\n[data-supertokens~="button"]:disabled {\n border: none;\n cursor: no-drop;\n}\n\n[data-supertokens~="button"]:active {\n outline: none;\n transition: all 0s;\n background-size: 100%;\n -webkit-filter: brightness(0.85);\n filter: brightness(0.85);\n}\n\n[data-supertokens~="button"]:focus {\n outline: none;\n}\n\n[data-supertokens~="backButtonCommon"] {\n width: 16px;\n height: 13px;\n}\n\n[data-supertokens~="backButton"] {\n cursor: pointer;\n border: none;\n background-color: transparent;\n padding: 0px;\n}\n\n[data-supertokens~="backButtonPlaceholder"] {\n display: block;\n}\n\n/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.\n *\n * This software is licensed under the Apache License, Version 2.0 (the\n * "License") as published by the Apache Software Foundation.\n *\n * You may not use this file except in compliance with the License. You may\n * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations\n * under the License.\n */\n\n[data-supertokens~="inputContainer"] {\n margin-top: 6px;\n}\n\n[data-supertokens~="inputWrapper"] {\n box-sizing: border-box;\n width: 100%;\n display: flex;\n align-items: center;\n background-color: rgb(var(--palette-inputBackground));\n height: 34px;\n border-radius: 6px;\n border: 1px solid rgb(var(--palette-inputBorder));\n}\n\n[data-supertokens~="inputWrapper"][focus-within] {\n background-color: rgba(var(--palette-inputBackground), 0.25);\n border: 1px solid rgb(var(--palette-primary));\n box-shadow: 0 0 0 0.2rem rgba(var(--palette-primary), 0.25);\n outline: none;\n}\n\n[data-supertokens~="inputWrapper"]:focus-within {\n background-color: rgba(var(--palette-inputBackground), 0.25);\n border: 1px solid rgb(var(--palette-primary));\n box-shadow: 0 0 0 0.2rem rgba(var(--palette-primary), 0.25);\n outline: none;\n}\n\n[data-supertokens~="inputError"] {\n border: 1px solid rgb(var(--palette-error));\n box-shadow: 0 0 0 0.2rem rgba(var(--palette-error), 0.25);\n outline: none;\n}\n\n[data-supertokens~="inputError"][focus-within] {\n border: 1px solid rgb(var(--palette-error));\n box-shadow: 0 0 0 0.2rem rgba(var(--palette-error), 0.25);\n outline: none;\n}\n\n[data-supertokens~="inputError"]:focus-within {\n border: 1px solid rgb(var(--palette-error));\n box-shadow: 0 0 0 0.2rem rgba(var(--palette-error), 0.25);\n outline: none;\n}\n\n[data-supertokens~="input"] {\n box-sizing: border-box;\n padding-left: 15px;\n -webkit-filter: none;\n filter: none;\n color: rgb(var(--palette-textInput));\n background-color: transparent;\n border-radius: 6px;\n font-size: var(--font-size-1);\n border: none;\n padding-right: 25px;\n letter-spacing: 1.2px;\n flex: 9 1 75%;\n width: 75%;\n height: 32px;\n}\n\n[data-supertokens~="input"]:focus {\n border: none;\n outline: none;\n}\n\n[data-supertokens~="input"]:-webkit-autofill,\n[data-supertokens~="input"]:-webkit-autofill:hover,\n[data-supertokens~="input"]:-webkit-autofill:focus,\n[data-supertokens~="input"]:-webkit-autofill:active {\n -webkit-text-fill-color: rgb(var(--palette-textInput));\n box-shadow: 0 0 0 30px rgb(var(--palette-inputBackground)) inset;\n}\n\n[data-supertokens~="inputAdornment"] {\n justify-content: center;\n margin-right: 5px;\n}\n\n[data-supertokens~="showPassword"] {\n cursor: pointer;\n}\n\n[data-supertokens~="forgotPasswordLink"] {\n margin-top: 10px;\n}\n\n[data-supertokens~="enterEmailSuccessMessage"] {\n margin-top: 15px;\n margin-bottom: 15px;\n word-break: break-word;\n}\n\n[data-supertokens~="submitNewPasswordSuccessMessage"] {\n margin-top: 15px;\n margin-bottom: 15px;\n}\n\n[data-supertokens~="inputErrorMessage"] {\n padding-top: 5px;\n padding-bottom: 5px;\n color: rgb(var(--palette-error));\n line-height: 24px;\n font-weight: 400;\n font-size: var(--font-size-1);\n text-align: left;\n -webkit-animation: slideTop 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;\n animation: slideTop 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;\n max-width: 330px;\n}\n\n@media (max-width: 440px) {\n [data-supertokens~="inputErrorMessage"] {\n max-width: 250px;\n }\n}\n\n[data-supertokens~="inputErrorSymbol"] {\n margin-right: 5px;\n top: 1px;\n position: relative;\n left: 2px;\n}\n\n[data-supertokens~="label"] {\n text-align: left;\n font-weight: 600;\n font-size: var(--font-size-1);\n line-height: 24px;\n color: rgb(var(--palette-textLabel));\n}\n\n[data-supertokens~="formRow"] {\n display: flex;\n flex-direction: column;\n padding-top: 0px;\n padding-bottom: 34px;\n}\n\n[data-supertokens~="formRow"][data-supertokens~="hasError"] {\n padding-bottom: 0;\n}\n\n[data-supertokens~="sendVerifyEmailIcon"] {\n margin-top: 11px;\n}\n\n[data-supertokens~="headerTinyTitle"] {\n margin-top: 13px;\n font-size: var(--font-size-3);\n letter-spacing: 1.1px;\n font-weight: 500;\n line-height: 28px;\n}\n\n[data-supertokens~="sendVerifyEmailText"] {\n line-height: 21px;\n font-size: var(--font-size-1);\n text-align: center;\n font-weight: 300;\n letter-spacing: 0.8px;\n}\n\n[data-supertokens~="secondaryLinkWithArrow"] {\n margin-top: 10px;\n margin-bottom: 30px;\n cursor: pointer;\n}\n\n[data-supertokens~="secondaryLinkWithArrow"]:hover {\n position: relative;\n left: 2px;\n word-spacing: 4px;\n}\n\n[data-supertokens~="sendVerifyEmailResend"] {\n margin-top: 13px;\n font-weight: 300;\n}\n\n[data-supertokens~="sendVerifyEmailResend"]:hover {\n text-decoration: underline;\n}\n\n[data-supertokens~="noFormRow"] {\n padding-bottom: 25px;\n}\n\n[data-supertokens~="emailVerificationButtonWrapper"] {\n padding-top: 25px;\n max-width: 96px;\n margin: 0 auto;\n}\n\n[data-supertokens~="withBackButton"] {\n position: relative;\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n[data-supertokens~="resendEmailLink"] {\n display: inline-block;\n}\n\n[data-supertokens~="generalSuccess"] {\n margin-bottom: 20px;\n -webkit-animation: swingIn 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) alternate 2 both;\n animation: swingIn 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) alternate 2 both;\n}\n\n[data-supertokens~="codeInputLabelWrapper"] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n[data-supertokens~="headerSubtitle"] strong {\n max-width: 100%;\n display: inline-block;\n vertical-align: bottom;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n[data-supertokens~="sendCodeText"] {\n margin-top: 15px;\n margin-bottom: 20px;\n}\n\n[data-supertokens~="sendCodeText"] strong {\n max-width: 100%;\n display: inline-block;\n vertical-align: bottom;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n[data-supertokens~="resendCodeBtn"] {\n width: auto;\n margin-top: 0;\n line-height: 24px;\n}\n\n[data-supertokens~="resendCodeBtn"]:hover {\n text-decoration: underline;\n}\n\n[data-supertokens~="resendCodeBtn"]:disabled {\n color: rgb(var(--palette-textPrimary));\n cursor: default;\n text-decoration: none;\n}\n\n[data-supertokens~="phoneInputLibRoot"] {\n display: flex;\n align-items: center;\n}\n\n[data-supertokens~="phoneInputWrapper"] {\n display: flex;\n align-items: center;\n}\n\n[data-supertokens~="phoneInputWrapper"] .iti [data-supertokens~="input"] {\n padding-left: 15px;\n}\n\n[data-supertokens~="phoneInputWrapper"] .iti {\n flex: 1 1;\n min-width: 0;\n width: 100%;\n background: transparent;\n border: none;\n color: inherit;\n outline: none;\n}\n\n[data-supertokens~="continueButtonWrapper"] {\n margin-top: 10px;\n margin-bottom: 30px;\n}\n\n[data-supertokens~="footerLinkGroup"] {\n display: flex;\n justify-content: space-between;\n margin-top: 20px;\n gap: 8px;\n}\n\n[data-supertokens~="footerLinkGroup"] > div {\n margin: 0;\n}\n\n@media (max-width: 360px) {\n [data-supertokens~="footerLinkGroup"] {\n flex-direction: column;\n }\n [data-supertokens~="footerLinkGroup"] > div {\n margin: 0 auto;\n }\n}\n\n[data-supertokens~="footerLinkGroup"] div:only-child {\n margin-left: auto;\n margin-right: auto;\n}\n\n.iti__country-list {\n border: 0;\n top: 40px;\n width: min(72.2vw, 320px);\n border-radius: 6;\n box-shadow: 0px 0px 3px 0px rgba(0, 0, 0, 0.16);\n}\n\n.iti__country {\n display: flex;\n align-items: center;\n height: 34px;\n cursor: pointer;\n\n padding: 0 8px;\n}\n\n.iti__country-name {\n color: var(--palette-textLabel);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n margin: "0 16px";\n}\n'; var ThemeBase = function (_a) { var children = _a.children, @@ -231,6 +232,7 @@ var defaultTranslationsPasswordless = { PWLESS_MFA_HEADER_TITLE_EMAIL: "Email based OTP", PWLESS_MFA_FOOTER_CHOOSER_ANOTHER: "Choose another factor", PWLESS_MFA_FOOTER_LOGOUT: "Logout", + PWLESS_MFA_OTP_NOT_ALLOWED_TO_SETUP: "You are not allowed to set up OTP.", /* * The following are error messages from our backend SDK. * These are returned as full messages to preserver compatibilty, but they work just like the keys above. @@ -3133,43 +3135,52 @@ var MFAFooter = uiEntry.withOverride("PasswordlessMFAFooter", function Passwordl var _a, _b; var t = translationContext.useTranslation(); var claim = session.useClaimValue(multifactorauth.MultiFactorAuthClaim); - return jsxRuntime.jsxs(jsxRuntime.Fragment, { - children: [ - claim.loading === false && - ((_b = (_a = claim.value) === null || _a === void 0 ? void 0 : _a.n.length) !== null && _b !== void 0 - ? _b - : 0) > 1 && - jsxRuntime.jsx( - "div", - genericComponentOverrideContext.__assign( - { - "data-supertokens": "secondaryText secondaryLinkWithLeftArrow", - onClick: function () { - return props.onFactorChooserButtonClicked; + return jsxRuntime.jsxs( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "footerLinkGroup pwlessMFAFooter" }, + { + children: [ + claim.loading === false && + ((_b = (_a = claim.value) === null || _a === void 0 ? void 0 : _a.n.length) !== null && + _b !== void 0 + ? _b + : 0) > 1 && + jsxRuntime.jsx( + "div", + genericComponentOverrideContext.__assign( + { + "data-supertokens": "secondaryText secondaryLinkWithLeftArrow", + onClick: function () { + return props.onFactorChooserButtonClicked; + }, + }, + { children: t("PWLESS_MFA_FOOTER_CHOOSER_ANOTHER") } + ) + ), + jsxRuntime.jsxs( + "div", + genericComponentOverrideContext.__assign( + { + "data-supertokens": "secondaryText secondaryLinkWithLeftArrow", + onClick: function () { + return props.onSignOutClicked; + }, }, - }, - { children: t("PWLESS_MFA_FOOTER_CHOOSER_ANOTHER") } - ) - ), - jsxRuntime.jsxs( - "div", - genericComponentOverrideContext.__assign( - { - "data-supertokens": "secondaryText secondaryLinkWithLeftArrow", - onClick: function () { - return props.onSignOutClicked; - }, - }, - { - children: [ - jsxRuntime.jsx(arrowLeftIcon.ArrowLeftIcon, { color: "rgb(var(--palette-textPrimary))" }), - t("PWLESS_MFA_FOOTER_LOGOUT"), - ], - } - ) - ), - ], - }); + { + children: [ + jsxRuntime.jsx(arrowLeftIcon.ArrowLeftIcon, { + color: "rgb(var(--palette-textPrimary))", + }), + t("PWLESS_MFA_FOOTER_LOGOUT"), + ], + } + ) + ), + ], + } + ) + ); }); var MFAHeader = uiEntry.withOverride("PasswordlessMFAHeader", function PasswordlessMFAHeader(props) { @@ -3204,51 +3215,83 @@ var MFAHeader = uiEntry.withOverride("PasswordlessMFAHeader", function Passwordl }); var MFAOTPFooter = uiEntry.withOverride("PasswordlessMFAOTPFooter", function PasswordlessMFAOTPFooter(_a) { + var _b, _c; var loginAttemptInfo = _a.loginAttemptInfo, recipeImplementation = _a.recipeImplementation, onSignOutClicked = _a.onSignOutClicked, + onFactorChooserButtonClicked = _a.onFactorChooserButtonClicked, isSetupAllowed = _a.isSetupAllowed; var t = translationContext.useTranslation(); + var claim = session.useClaimValue(multifactorauth.MultiFactorAuthClaim); var userContext = uiEntry.useUserContext(); - return jsxRuntime.jsx(React.Fragment, { - children: - (isSetupAllowed && - jsxRuntime.jsxs( - "div", - genericComponentOverrideContext.__assign( - { - "data-supertokens": "secondaryText secondaryLinkWithLeftArrow", - onClick: function () { - return recipeImplementation.clearLoginAttemptInfo({ - userContext: userContext, - }); + return jsxRuntime.jsxs( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "footerLinkGroup pwlessMFAOTPFooter" }, + { + children: [ + isSetupAllowed + ? jsxRuntime.jsxs( + "div", + genericComponentOverrideContext.__assign( + { + "data-supertokens": "secondaryText secondaryLinkWithLeftArrow", + onClick: function () { + return recipeImplementation.clearLoginAttemptInfo({ + userContext: userContext, + }); + }, + }, + { + children: [ + jsxRuntime.jsx(arrowLeftIcon.ArrowLeftIcon, { + color: "rgb(var(--palette-textPrimary))", + }), + loginAttemptInfo.contactMethod === "EMAIL" + ? t("PWLESS_SIGN_IN_UP_CHANGE_CONTACT_INFO_EMAIL") + : t("PWLESS_SIGN_IN_UP_CHANGE_CONTACT_INFO_PHONE"), + ], + } + ) + ) + : claim.loading === false && + ((_c = (_b = claim.value) === null || _b === void 0 ? void 0 : _b.n.length) !== null && + _c !== void 0 + ? _c + : 0) > 1 && + jsxRuntime.jsx( + "div", + genericComponentOverrideContext.__assign( + { + "data-supertokens": "secondaryText secondaryLinkWithLeftArrow", + onClick: function () { + return onFactorChooserButtonClicked; + }, + }, + { children: t("PWLESS_MFA_FOOTER_CHOOSER_ANOTHER") } + ) + ), + jsxRuntime.jsxs( + "div", + genericComponentOverrideContext.__assign( + { + "data-supertokens": "secondaryText secondaryLinkWithLeftArrow", + onClick: onSignOutClicked, }, - }, - { - children: [ - jsxRuntime.jsx(arrowLeftIcon.ArrowLeftIcon, { - color: "rgb(var(--palette-textPrimary))", - }), - loginAttemptInfo.contactMethod === "EMAIL" - ? t("PWLESS_SIGN_IN_UP_CHANGE_CONTACT_INFO_EMAIL") - : t("PWLESS_SIGN_IN_UP_CHANGE_CONTACT_INFO_PHONE"), - ], - } - ) - )) || - jsxRuntime.jsxs( - "div", - genericComponentOverrideContext.__assign( - { "data-supertokens": "secondaryText secondaryLinkWithLeftArrow", onClick: onSignOutClicked }, - { - children: [ - jsxRuntime.jsx(arrowLeftIcon.ArrowLeftIcon, { color: "rgb(var(--palette-textPrimary))" }), - t("PWLESS_MFA_LOGOUT"), - ], - } - ) - ), - }); + { + children: [ + jsxRuntime.jsx(arrowLeftIcon.ArrowLeftIcon, { + color: "rgb(var(--palette-textPrimary))", + }), + t("PWLESS_MFA_LOGOUT"), + ], + } + ) + ), + ], + } + ) + ); }); var MFAOTPHeader = uiEntry.withOverride("PasswordlessMFAOTPHeader", function PasswordlessMFAOTPHeader(_a) { @@ -3305,6 +3348,7 @@ var MFAScreens; MFAScreens[(MFAScreens["EmailForm"] = 1)] = "EmailForm"; MFAScreens[(MFAScreens["PhoneForm"] = 2)] = "PhoneForm"; MFAScreens[(MFAScreens["UserInputCodeForm"] = 3)] = "UserInputCodeForm"; + MFAScreens[(MFAScreens["AccessDenied"] = 4)] = "AccessDenied"; })(MFAScreens || (MFAScreens = {})); /* * Component. @@ -3314,6 +3358,7 @@ var MFATheme = function (_a) { featureState = _a.featureState, onBackButtonClicked = _a.onBackButtonClicked, props = genericComponentOverrideContext.__rest(_a, ["activeScreen", "featureState", "onBackButtonClicked"]); + var t = translationContext.useTranslation(); var commonProps = { recipeImplementation: props.recipeImplementation, config: props.config, @@ -3327,6 +3372,11 @@ var MFATheme = function (_a) { }; return activeScreen === MFAScreens.CloseTab ? jsxRuntime.jsx(CloseTabScreen, genericComponentOverrideContext.__assign({}, commonProps)) + : activeScreen === MFAScreens.AccessDenied + ? jsxRuntime.jsx(sessionprebuiltui.AccessDeniedScreen, { + useShadowDom: props.config.useShadowDom, + error: t(featureState.error), + }) : jsxRuntime.jsxs( "div", genericComponentOverrideContext.__assign( @@ -3340,8 +3390,6 @@ var MFATheme = function (_a) { { children: featureState.loaded && - /* TODO: this doesn't feel great */ (featureState.isSetupAllowed === true || - featureState.loginAttemptInfo !== undefined) && jsxRuntime.jsxs(React__namespace.default.Fragment, { children: [ activeScreen === MFAScreens.UserInputCodeForm @@ -3455,6 +3503,9 @@ function MFAThemeWrapper(props) { activeStyle = props.config.signInUpFeature.emailOrPhoneFormStyle; } else if (activeScreen === MFAScreens.PhoneForm) { activeStyle = props.config.signInUpFeature.emailOrPhoneFormStyle; + } else { + // TODO: test + activeStyle = ""; // styling the access denied screen is handled through the session recipe } return jsxRuntime.jsx( uiEntry.UserContextWrapper, @@ -3480,7 +3531,13 @@ function MFAThemeWrapper(props) { function getActiveScreen$1(props) { if (props.featureState.successInAnotherTab) { return MFAScreens.CloseTab; - } else if (props.featureState.loginAttemptInfo) { + } else if ( + props.featureState.error === "PWLESS_MFA_OTP_NOT_ALLOWED_TO_SETUP" || + (props.featureState.isSetupAllowed === false && props.featureState.loginAttemptInfo === undefined) + // The first condition should always be true if the second one is true, this is here as a "fallback" + ) { + return MFAScreens.AccessDenied; + } else if (props.featureState.isSetupAllowed !== true && props.featureState.loginAttemptInfo) { return MFAScreens.UserInputCodeForm; } else if (props.contactMethod === "EMAIL") { return MFAScreens.EmailForm; @@ -3783,12 +3840,12 @@ function useOnLoad(props, recipeImplementation, dispatch, userContext) { [props.recipe, userContext] ); var handleLoadError = React__namespace.useCallback( - // Test this, it may show an empty screen in many cases + // TODO: Test this, it may show an empty screen in many cases function () { - return dispatch({ type: "setError", error: "Getting mfaInfo failed!" }); + return dispatch({ type: "setError", error: "SOMETHING_WENT_WRONG_ERROR" }); }, [dispatch] - ); // TODO: translation/proper error handling) + ); var onLoad = React__namespace.useCallback( function (mfaInfo) { return genericComponentOverrideContext.__awaiter(_this, void 0, void 0, function () { @@ -3848,7 +3905,12 @@ function useOnLoad(props, recipeImplementation, dispatch, userContext) { return [3 /*break*/, 4]; case 3: if (!mfaInfo.factors.isAllowedToSetup.includes("otp-email")) { - dispatch({ type: "setError", error: "Factor not enabled" }); // TODO: translation + dispatch({ + type: "load", + loginAttemptInfo: loginAttemptInfo, + isAllowedToSetup: false, + error: "PWLESS_MFA_OTP_NOT_ALLOWED_TO_SETUP", + }); } else { dispatch({ type: "load", @@ -3876,7 +3938,12 @@ function useOnLoad(props, recipeImplementation, dispatch, userContext) { return [3 /*break*/, 8]; case 7: if (!mfaInfo.factors.isAllowedToSetup.includes("otp-phone")) { - dispatch({ type: "setError", error: "Factor not enabled" }); // TODO: translation + dispatch({ + type: "load", + loginAttemptInfo: loginAttemptInfo, + isAllowedToSetup: false, + error: "PWLESS_MFA_OTP_NOT_ALLOWED_TO_SETUP", + }); } else { dispatch({ type: "load", diff --git a/lib/build/passwordlessprebuiltui.js b/lib/build/passwordlessprebuiltui.js index 86d6b0a67..ce3dbc180 100644 --- a/lib/build/passwordlessprebuiltui.js +++ b/lib/build/passwordlessprebuiltui.js @@ -32,10 +32,11 @@ require("./translations.js"); require("./emailpassword-shared2.js"); require("./SuperTokensBranding.js"); require("./emailpassword-shared.js"); +require("./sessionprebuiltui.js"); +require("./arrowLeftIcon.js"); require("./checkedRoundIcon.js"); require("./emailpassword-shared8.js"); require("./emailpassword-shared5.js"); -require("./arrowLeftIcon.js"); require("./multifactorauth-shared2.js"); require("./emailpassword-shared7.js"); require("supertokens-web-js/recipe/passwordless"); diff --git a/lib/build/recipe/passwordless/components/themes/mfa/index.d.ts b/lib/build/recipe/passwordless/components/themes/mfa/index.d.ts index 78c70fdbf..bdc60d2d1 100644 --- a/lib/build/recipe/passwordless/components/themes/mfa/index.d.ts +++ b/lib/build/recipe/passwordless/components/themes/mfa/index.d.ts @@ -5,6 +5,7 @@ export declare enum MFAScreens { EmailForm = 1, PhoneForm = 2, UserInputCodeForm = 3, + AccessDenied = 4, } declare function MFAThemeWrapper(props: MFAProps): JSX.Element; export default MFAThemeWrapper; diff --git a/lib/build/recipe/passwordless/components/themes/translations.d.ts b/lib/build/recipe/passwordless/components/themes/translations.d.ts index 68e12c3a3..78ccc297c 100644 --- a/lib/build/recipe/passwordless/components/themes/translations.d.ts +++ b/lib/build/recipe/passwordless/components/themes/translations.d.ts @@ -57,6 +57,7 @@ export declare const defaultTranslationsPasswordless: { PWLESS_MFA_HEADER_TITLE_EMAIL: string; PWLESS_MFA_FOOTER_CHOOSER_ANOTHER: string; PWLESS_MFA_FOOTER_LOGOUT: string; + PWLESS_MFA_OTP_NOT_ALLOWED_TO_SETUP: string; "Failed to generate a one time code. Please try again": undefined; "Phone number is invalid": undefined; "Email is invalid": undefined; diff --git a/lib/build/recipe/passwordless/recipe.d.ts b/lib/build/recipe/passwordless/recipe.d.ts index b3f88bc60..ef693e2a5 100644 --- a/lib/build/recipe/passwordless/recipe.d.ts +++ b/lib/build/recipe/passwordless/recipe.d.ts @@ -1,4 +1,6 @@ +/// import PasswordlessWebJS from "supertokens-web-js/recipe/passwordless"; +import NormalisedURLPath from "supertokens-web-js/utils/normalisedURLPath"; import AuthRecipe from "../authRecipe"; import type { GetRedirectionURLContext, @@ -8,6 +10,20 @@ import type { UserInput, } from "./types"; import type { RecipeInitResult, NormalisedConfigWithAppInfoAndRecipeID, WebJSRecipeInterface } from "../../types"; +export declare const otpPhoneFactor: { + id: string; + name: string; + description: string; + path: NormalisedURLPath; + logo: () => JSX.Element; +}; +export declare const otpEmailFactor: { + id: string; + name: string; + description: string; + path: NormalisedURLPath; + logo: () => JSX.Element; +}; export default class Passwordless extends AuthRecipe< GetRedirectionURLContext, PreAndPostAPIHookAction, diff --git a/lib/build/recipe/thirdpartypasswordless/components/themes/translations.d.ts b/lib/build/recipe/thirdpartypasswordless/components/themes/translations.d.ts index 8afcc48ec..14113e5ca 100644 --- a/lib/build/recipe/thirdpartypasswordless/components/themes/translations.d.ts +++ b/lib/build/recipe/thirdpartypasswordless/components/themes/translations.d.ts @@ -59,6 +59,7 @@ export declare const defaultTranslationsThirdPartyPasswordless: { PWLESS_MFA_HEADER_TITLE_EMAIL: string; PWLESS_MFA_FOOTER_CHOOSER_ANOTHER: string; PWLESS_MFA_FOOTER_LOGOUT: string; + PWLESS_MFA_OTP_NOT_ALLOWED_TO_SETUP: string; "Failed to generate a one time code. Please try again": undefined; "Phone number is invalid": undefined; "Email is invalid": undefined; diff --git a/lib/build/thirdpartypasswordlessprebuiltui.js b/lib/build/thirdpartypasswordlessprebuiltui.js index 8c61dc76c..b2709e7a6 100644 --- a/lib/build/thirdpartypasswordlessprebuiltui.js +++ b/lib/build/thirdpartypasswordlessprebuiltui.js @@ -36,10 +36,11 @@ require("./emailpassword-shared2.js"); require("./passwordless-shared2.js"); require("supertokens-web-js/recipe/passwordless"); require("./authRecipe-shared.js"); +require("./sessionprebuiltui.js"); +require("./arrowLeftIcon.js"); require("./checkedRoundIcon.js"); require("./emailpassword-shared8.js"); require("./emailpassword-shared5.js"); -require("./arrowLeftIcon.js"); require("./multifactorauth-shared2.js"); require("./emailpassword-shared7.js"); require("./thirdparty-shared.js"); @@ -81,7 +82,7 @@ var NormalisedURLPath__default = /*#__PURE__*/ _interopDefault(NormalisedURLPath var React__namespace = /*#__PURE__*/ _interopNamespace(React); var styles = - '/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.\n *\n * This software is licensed under the Apache License, Version 2.0 (the\n * "License") as published by the Apache Software Foundation.\n *\n * You may not use this file except in compliance with the License. You may\n * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations\n * under the License.\n */\n/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.\n *\n * This software is licensed under the Apache License, Version 2.0 (the\n * "License") as published by the Apache Software Foundation.\n *\n * You may not use this file except in compliance with the License. You may\n * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations\n * under the License.\n */\n[data-supertokens~="container"] {\n --palette-background: 255, 255, 255;\n --palette-inputBackground: 250, 250, 250;\n --palette-inputBorder: 224, 224, 224;\n --palette-primary: 255, 155, 51;\n --palette-primaryBorder: 238, 141, 35;\n --palette-success: 65, 167, 0;\n --palette-successBackground: 217, 255, 191;\n --palette-error: 255, 23, 23;\n --palette-errorBackground: 255, 241, 235;\n --palette-textTitle: 34, 34, 34;\n --palette-textLabel: 34, 34, 34;\n --palette-textInput: 34, 34, 34;\n --palette-textPrimary: 101, 101, 101;\n --palette-textLink: 0, 118, 255;\n --palette-buttonText: 255, 255, 255;\n --palette-textGray: 128, 128, 128;\n --palette-superTokensBrandingBackground: 242, 245, 246;\n --palette-superTokensBrandingText: 173, 189, 196;\n\n --font-size-0: 12px;\n --font-size-1: 14px;\n --font-size-2: 16px;\n --font-size-3: 19px;\n --font-size-4: 24px;\n}\n/*\n * Default styles.\n */\n@-webkit-keyframes slideTop {\n 0% {\n -webkit-transform: translateY(-5px);\n transform: translateY(-5px);\n }\n 100% {\n -webkit-transform: translateY(0px);\n transform: translateY(0px);\n }\n}\n@keyframes slideTop {\n 0% {\n -webkit-transform: translateY(-5px);\n transform: translateY(-5px);\n }\n 100% {\n -webkit-transform: translateY(0px);\n transform: translateY(0px);\n }\n}\n@-webkit-keyframes swing-in-top-fwd {\n 0% {\n -webkit-transform: rotateX(-100deg);\n transform: rotateX(-100deg);\n -webkit-transform-origin: top;\n transform-origin: top;\n opacity: 0;\n }\n 100% {\n -webkit-transform: rotateX(0deg);\n transform: rotateX(0deg);\n -webkit-transform-origin: top;\n transform-origin: top;\n opacity: 1;\n }\n}\n@keyframes swing-in-top-fwd {\n 0% {\n -webkit-transform: rotateX(-100deg);\n transform: rotateX(-100deg);\n -webkit-transform-origin: top;\n transform-origin: top;\n opacity: 0;\n }\n 100% {\n -webkit-transform: rotateX(0deg);\n transform: rotateX(0deg);\n -webkit-transform-origin: top;\n transform-origin: top;\n opacity: 1;\n }\n}\n[data-supertokens~="container"] {\n font-family: "Rubik", sans-serif;\n margin: 12px auto;\n margin-top: 26px;\n margin-bottom: 26px;\n width: 420px;\n text-align: center;\n border-radius: 8px;\n box-shadow: 1px 1px 10px rgba(0, 0, 0, 0.16);\n background-color: rgb(var(--palette-background));\n}\n@media (max-width: 440px) {\n [data-supertokens~="container"] {\n width: 95vw;\n }\n}\n[data-supertokens~="row"] {\n margin: 0 auto;\n width: 76%;\n padding-top: 30px;\n padding-bottom: 10px;\n}\n[data-supertokens~="superTokensBranding"] {\n display: block;\n margin: 0 auto;\n background: rgb(var(--palette-superTokensBrandingBackground));\n color: rgb(var(--palette-superTokensBrandingText));\n text-decoration: none;\n width: -webkit-fit-content;\n width: -moz-fit-content;\n width: fit-content;\n border-radius: 6px 6px 0 0;\n padding: 4px 9px;\n font-weight: 300;\n font-size: var(--font-size-0);\n letter-spacing: 0.4px;\n}\n[data-supertokens~="generalError"] {\n background: rgb(var(--palette-errorBackground));\n padding-top: 10px;\n padding-bottom: 10px;\n margin-bottom: 15px;\n padding-left: 18px;\n padding-right: 18px;\n letter-spacing: 0.2px;\n font-size: var(--font-size-1);\n border-radius: 8px;\n color: rgb(var(--palette-error));\n -webkit-animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;\n animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;\n word-wrap: break-word;\n}\n[data-supertokens~="headerTitle"] {\n font-size: var(--font-size-4);\n line-height: 40px;\n letter-spacing: 0.58px;\n font-weight: 800;\n margin-bottom: 2px;\n color: rgb(var(--palette-textTitle));\n}\n[data-supertokens~="headerSubtitle"] {\n margin-bottom: 21px;\n}\n[data-supertokens~="privacyPolicyAndTermsAndConditions"] {\n max-width: 300px;\n margin-top: 10px;\n}\n[data-supertokens~="privacyPolicyAndTermsAndConditions"] a {\n line-height: 21px;\n}\n/* TODO: split the link style into separate things*/\n/* We add this before primary and secondary text, because if they are applied to the same element the other ones take priority */\n[data-supertokens~="link"] {\n padding-left: 3px;\n padding-right: 3px;\n color: rgb(var(--palette-textLink));\n font-size: var(--font-size-1);\n cursor: pointer;\n letter-spacing: 0.16px;\n line-height: 26px;\n}\n[data-supertokens~="primaryText"] {\n font-size: var(--font-size-1);\n font-weight: 500;\n letter-spacing: 0.4px;\n line-height: 21px;\n color: rgb(var(--palette-textLabel));\n}\n[data-supertokens~="secondaryText"] {\n font-size: var(--font-size-1);\n font-weight: 300;\n letter-spacing: 0.4px;\n color: rgb(var(--palette-textPrimary));\n}\n[data-supertokens~="divider"] {\n margin-top: 1em;\n margin-bottom: 1em;\n border-bottom: 0.3px solid #dddddd;\n align-items: center;\n padding-bottom: 5px;\n}\n[data-supertokens~="headerTinyTitle"] {\n margin-top: 13px;\n font-size: var(--font-size-3);\n letter-spacing: 1.1px;\n font-weight: 500;\n line-height: 28px;\n}\n[data-supertokens~="secondaryLinkWithArrow"] {\n margin-top: 10px;\n margin-bottom: 30px;\n cursor: pointer;\n}\n[data-supertokens~="secondaryLinkWithArrow"]:hover {\n position: relative;\n left: 2px;\n word-spacing: 4px;\n}\n[data-supertokens~="generalSuccess"] {\n color: rgb(var(--palette-success));\n font-size: var(--font-size-1);\n background: rgb(var(--palette-successBackground));\n -webkit-animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;\n animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;\n padding: 9px 15px 9px 15px;\n border-radius: 6px;\n display: inline-block;\n}\n[data-supertokens~="spinner"] {\n width: 80px;\n height: auto;\n padding-top: 20px;\n padding-bottom: 40px;\n margin: 0 auto;\n}\n[data-supertokens~="error"] {\n color: rgb(var(--palette-error));\n}\n[data-supertokens~="linkButton"] {\n background-color: transparent;\n border: 0;\n}\n[data-supertokens~="secondaryLinkWithLeftArrow"] {\n margin-top: 10px;\n margin-bottom: 40px;\n cursor: pointer;\n}\n[data-supertokens~="secondaryLinkWithLeftArrow"] svg {\n margin-right: 0.3em;\n}\n[data-supertokens~="secondaryLinkWithLeftArrow"]:hover svg {\n position: relative;\n left: -4px;\n}\n[data-supertokens~="button"] {\n background-color: rgb(var(--palette-primary));\n color: rgb(var(--palette-buttonText));\n width: 100%;\n height: 34px;\n font-weight: 700;\n border-width: 1px;\n border-style: solid;\n border-radius: 6px;\n border-color: rgb(var(--palette-primaryBorder));\n background-position: center;\n transition: all 0.4s;\n background-size: 12000%;\n cursor: pointer;\n}\n[data-supertokens~="button"]:disabled {\n border: none;\n cursor: no-drop;\n}\n[data-supertokens~="button"]:active {\n outline: none;\n transition: all 0s;\n background-size: 100%;\n -webkit-filter: brightness(0.85);\n filter: brightness(0.85);\n}\n[data-supertokens~="button"]:focus {\n outline: none;\n}\n[data-supertokens~="backButtonCommon"] {\n width: 16px;\n height: 13px;\n}\n[data-supertokens~="backButton"] {\n cursor: pointer;\n border: none;\n background-color: transparent;\n padding: 0px;\n}\n[data-supertokens~="backButtonPlaceholder"] {\n display: block;\n}\n/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.\n *\n * This software is licensed under the Apache License, Version 2.0 (the\n * "License") as published by the Apache Software Foundation.\n *\n * You may not use this file except in compliance with the License. You may\n * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations\n * under the License.\n */\n[data-supertokens~="inputContainer"] {\n margin-top: 6px;\n}\n[data-supertokens~="inputWrapper"] {\n box-sizing: border-box;\n width: 100%;\n display: flex;\n align-items: center;\n background-color: rgb(var(--palette-inputBackground));\n height: 34px;\n border-radius: 6px;\n border: 1px solid rgb(var(--palette-inputBorder));\n}\n[data-supertokens~="inputWrapper"][focus-within] {\n background-color: rgba(var(--palette-inputBackground), 0.25);\n border: 1px solid rgb(var(--palette-primary));\n box-shadow: 0 0 0 0.2rem rgba(var(--palette-primary), 0.25);\n outline: none;\n}\n[data-supertokens~="inputWrapper"]:focus-within {\n background-color: rgba(var(--palette-inputBackground), 0.25);\n border: 1px solid rgb(var(--palette-primary));\n box-shadow: 0 0 0 0.2rem rgba(var(--palette-primary), 0.25);\n outline: none;\n}\n[data-supertokens~="inputError"] {\n border: 1px solid rgb(var(--palette-error));\n box-shadow: 0 0 0 0.2rem rgba(var(--palette-error), 0.25);\n outline: none;\n}\n[data-supertokens~="inputError"][focus-within] {\n border: 1px solid rgb(var(--palette-error));\n box-shadow: 0 0 0 0.2rem rgba(var(--palette-error), 0.25);\n outline: none;\n}\n[data-supertokens~="inputError"]:focus-within {\n border: 1px solid rgb(var(--palette-error));\n box-shadow: 0 0 0 0.2rem rgba(var(--palette-error), 0.25);\n outline: none;\n}\n[data-supertokens~="input"] {\n box-sizing: border-box;\n padding-left: 15px;\n -webkit-filter: none;\n filter: none;\n color: rgb(var(--palette-textInput));\n background-color: transparent;\n border-radius: 6px;\n font-size: var(--font-size-1);\n border: none;\n padding-right: 25px;\n letter-spacing: 1.2px;\n flex: 9 1 75%;\n width: 75%;\n height: 32px;\n}\n[data-supertokens~="input"]:focus {\n border: none;\n outline: none;\n}\n[data-supertokens~="input"]:-webkit-autofill,\n[data-supertokens~="input"]:-webkit-autofill:hover,\n[data-supertokens~="input"]:-webkit-autofill:focus,\n[data-supertokens~="input"]:-webkit-autofill:active {\n -webkit-text-fill-color: rgb(var(--palette-textInput));\n box-shadow: 0 0 0 30px rgb(var(--palette-inputBackground)) inset;\n}\n[data-supertokens~="inputAdornment"] {\n justify-content: center;\n margin-right: 5px;\n}\n[data-supertokens~="showPassword"] {\n cursor: pointer;\n}\n[data-supertokens~="forgotPasswordLink"] {\n margin-top: 10px;\n}\n[data-supertokens~="enterEmailSuccessMessage"] {\n margin-top: 15px;\n margin-bottom: 15px;\n word-break: break-word;\n}\n[data-supertokens~="submitNewPasswordSuccessMessage"] {\n margin-top: 15px;\n margin-bottom: 15px;\n}\n[data-supertokens~="inputErrorMessage"] {\n padding-top: 5px;\n padding-bottom: 5px;\n color: rgb(var(--palette-error));\n line-height: 24px;\n font-weight: 400;\n font-size: var(--font-size-1);\n text-align: left;\n -webkit-animation: slideTop 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;\n animation: slideTop 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;\n max-width: 330px;\n}\n@media (max-width: 440px) {\n [data-supertokens~="inputErrorMessage"] {\n max-width: 250px;\n }\n}\n[data-supertokens~="inputErrorSymbol"] {\n margin-right: 5px;\n top: 1px;\n position: relative;\n left: 2px;\n}\n[data-supertokens~="label"] {\n text-align: left;\n font-weight: 600;\n font-size: var(--font-size-1);\n line-height: 24px;\n color: rgb(var(--palette-textLabel));\n}\n[data-supertokens~="formRow"] {\n display: flex;\n flex-direction: column;\n padding-top: 0px;\n padding-bottom: 34px;\n}\n[data-supertokens~="formRow"][data-supertokens~="hasError"] {\n padding-bottom: 0;\n}\n[data-supertokens~="sendVerifyEmailIcon"] {\n margin-top: 11px;\n}\n[data-supertokens~="headerTinyTitle"] {\n margin-top: 13px;\n font-size: var(--font-size-3);\n letter-spacing: 1.1px;\n font-weight: 500;\n line-height: 28px;\n}\n[data-supertokens~="sendVerifyEmailText"] {\n line-height: 21px;\n font-size: var(--font-size-1);\n text-align: center;\n font-weight: 300;\n letter-spacing: 0.8px;\n}\n[data-supertokens~="secondaryLinkWithArrow"] {\n margin-top: 10px;\n margin-bottom: 30px;\n cursor: pointer;\n}\n[data-supertokens~="secondaryLinkWithArrow"]:hover {\n position: relative;\n left: 2px;\n word-spacing: 4px;\n}\n[data-supertokens~="sendVerifyEmailResend"] {\n margin-top: 13px;\n font-weight: 300;\n}\n[data-supertokens~="sendVerifyEmailResend"]:hover {\n text-decoration: underline;\n}\n[data-supertokens~="noFormRow"] {\n padding-bottom: 25px;\n}\n[data-supertokens~="emailVerificationButtonWrapper"] {\n padding-top: 25px;\n max-width: 96px;\n margin: 0 auto;\n}\n[data-supertokens~="withBackButton"] {\n position: relative;\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n[data-supertokens~="resendEmailLink"] {\n display: inline-block;\n}\n[data-supertokens~="generalSuccess"] {\n margin-bottom: 20px;\n -webkit-animation: swingIn 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) alternate 2 both;\n animation: swingIn 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) alternate 2 both;\n}\n[data-supertokens~="codeInputLabelWrapper"] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n[data-supertokens~="headerSubtitle"] strong {\n max-width: 100%;\n display: inline-block;\n vertical-align: bottom;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n[data-supertokens~="sendCodeText"] {\n margin-top: 15px;\n margin-bottom: 20px;\n}\n[data-supertokens~="sendCodeText"] strong {\n max-width: 100%;\n display: inline-block;\n vertical-align: bottom;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n[data-supertokens~="resendCodeBtn"] {\n width: auto;\n margin-top: 0;\n line-height: 24px;\n}\n[data-supertokens~="resendCodeBtn"]:hover {\n text-decoration: underline;\n}\n[data-supertokens~="resendCodeBtn"]:disabled {\n color: rgb(var(--palette-textPrimary));\n cursor: default;\n text-decoration: none;\n}\n[data-supertokens~="phoneInputLibRoot"] {\n display: flex;\n align-items: center;\n}\n[data-supertokens~="phoneInputWrapper"] {\n display: flex;\n align-items: center;\n}\n[data-supertokens~="phoneInputWrapper"] .iti [data-supertokens~="input"] {\n padding-left: 15px;\n}\n[data-supertokens~="phoneInputWrapper"] .iti {\n flex: 1 1;\n min-width: 0;\n width: 100%;\n background: transparent;\n border: none;\n color: inherit;\n outline: none;\n}\n[data-supertokens~="continueButtonWrapper"] {\n margin-top: 10px;\n margin-bottom: 30px;\n}\n.iti__country-list {\n border: 0;\n top: 40px;\n width: min(72.2vw, 320px);\n border-radius: 6;\n box-shadow: 0px 0px 3px 0px rgba(0, 0, 0, 0.16);\n}\n.iti__country {\n display: flex;\n align-items: center;\n height: 34px;\n cursor: pointer;\n\n padding: 0 8px;\n}\n.iti__country-name {\n color: var(--palette-textLabel);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n margin: "0 16px";\n}\n[data-supertokens~="row"] {\n padding-bottom: 30px;\n}\n[data-supertokens~="providerContainer"] {\n padding-top: 9px;\n padding-bottom: 9px;\n}\n[data-supertokens~="providerButton"] {\n border-color: rgb(221, 221, 221) !important;\n}\n[data-supertokens~="providerButton"] {\n min-height: 32px;\n display: flex;\n flex-direction: row;\n align-items: center;\n padding: 2px 8px;\n\n background-color: white;\n color: black;\n}\n[data-supertokens~="providerButton"]:hover {\n -webkit-filter: none !important;\n filter: none !important;\n}\n[data-supertokens~="providerButton"]:hover {\n background-color: #fafafa;\n}\n[data-supertokens~="providerButtonLeft"] {\n min-width: 34px;\n margin-left: 66px;\n}\n[data-supertokens~="providerButtonLogo"] {\n height: 30px;\n display: flex;\n}\n[data-supertokens~="providerButtonLogoCenter"] {\n display: flex;\n margin: auto;\n}\n[data-supertokens~="providerButtonText"] {\n font-weight: 400;\n text-align: center;\n justify-content: center;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n display: inline-block;\n}\n[data-supertokens~="providerButtonText"]:only-child {\n margin: 0 auto;\n}\n[data-supertokens~="thirdPartyPasswordlessDivider"] {\n padding-top: 5px;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n color: rgb(var(--palette-textPrimary));\n}\n[data-supertokens~="thirdPartyPasswordlessDividerText"] {\n flex: 1 1;\n margin-top: 0.75em;\n}\n[data-supertokens~="divider"] {\n flex: 3 3;\n}\n[data-supertokens~="providerButton"] {\n margin: auto !important;\n max-width: 240px !important;\n}\n[data-supertokens~="providerButtonLeft"] {\n margin-left: 30px !important;\n}\n'; + '/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.\n *\n * This software is licensed under the Apache License, Version 2.0 (the\n * "License") as published by the Apache Software Foundation.\n *\n * You may not use this file except in compliance with the License. You may\n * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations\n * under the License.\n */\n/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.\n *\n * This software is licensed under the Apache License, Version 2.0 (the\n * "License") as published by the Apache Software Foundation.\n *\n * You may not use this file except in compliance with the License. You may\n * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations\n * under the License.\n */\n[data-supertokens~="container"] {\n --palette-background: 255, 255, 255;\n --palette-inputBackground: 250, 250, 250;\n --palette-inputBorder: 224, 224, 224;\n --palette-primary: 255, 155, 51;\n --palette-primaryBorder: 238, 141, 35;\n --palette-success: 65, 167, 0;\n --palette-successBackground: 217, 255, 191;\n --palette-error: 255, 23, 23;\n --palette-errorBackground: 255, 241, 235;\n --palette-textTitle: 34, 34, 34;\n --palette-textLabel: 34, 34, 34;\n --palette-textInput: 34, 34, 34;\n --palette-textPrimary: 101, 101, 101;\n --palette-textLink: 0, 118, 255;\n --palette-buttonText: 255, 255, 255;\n --palette-textGray: 128, 128, 128;\n --palette-superTokensBrandingBackground: 242, 245, 246;\n --palette-superTokensBrandingText: 173, 189, 196;\n\n --font-size-0: 12px;\n --font-size-1: 14px;\n --font-size-2: 16px;\n --font-size-3: 19px;\n --font-size-4: 24px;\n}\n/*\n * Default styles.\n */\n@-webkit-keyframes slideTop {\n 0% {\n -webkit-transform: translateY(-5px);\n transform: translateY(-5px);\n }\n 100% {\n -webkit-transform: translateY(0px);\n transform: translateY(0px);\n }\n}\n@keyframes slideTop {\n 0% {\n -webkit-transform: translateY(-5px);\n transform: translateY(-5px);\n }\n 100% {\n -webkit-transform: translateY(0px);\n transform: translateY(0px);\n }\n}\n@-webkit-keyframes swing-in-top-fwd {\n 0% {\n -webkit-transform: rotateX(-100deg);\n transform: rotateX(-100deg);\n -webkit-transform-origin: top;\n transform-origin: top;\n opacity: 0;\n }\n 100% {\n -webkit-transform: rotateX(0deg);\n transform: rotateX(0deg);\n -webkit-transform-origin: top;\n transform-origin: top;\n opacity: 1;\n }\n}\n@keyframes swing-in-top-fwd {\n 0% {\n -webkit-transform: rotateX(-100deg);\n transform: rotateX(-100deg);\n -webkit-transform-origin: top;\n transform-origin: top;\n opacity: 0;\n }\n 100% {\n -webkit-transform: rotateX(0deg);\n transform: rotateX(0deg);\n -webkit-transform-origin: top;\n transform-origin: top;\n opacity: 1;\n }\n}\n[data-supertokens~="container"] {\n font-family: "Rubik", sans-serif;\n margin: 12px auto;\n margin-top: 26px;\n margin-bottom: 26px;\n width: 420px;\n text-align: center;\n border-radius: 8px;\n box-shadow: 1px 1px 10px rgba(0, 0, 0, 0.16);\n background-color: rgb(var(--palette-background));\n}\n@media (max-width: 440px) {\n [data-supertokens~="container"] {\n width: 95vw;\n }\n}\n[data-supertokens~="row"] {\n margin: 0 auto;\n width: 76%;\n padding-top: 30px;\n padding-bottom: 10px;\n}\n[data-supertokens~="superTokensBranding"] {\n display: block;\n margin: 0 auto;\n background: rgb(var(--palette-superTokensBrandingBackground));\n color: rgb(var(--palette-superTokensBrandingText));\n text-decoration: none;\n width: -webkit-fit-content;\n width: -moz-fit-content;\n width: fit-content;\n border-radius: 6px 6px 0 0;\n padding: 4px 9px;\n font-weight: 300;\n font-size: var(--font-size-0);\n letter-spacing: 0.4px;\n}\n[data-supertokens~="generalError"] {\n background: rgb(var(--palette-errorBackground));\n padding-top: 10px;\n padding-bottom: 10px;\n margin-bottom: 15px;\n padding-left: 18px;\n padding-right: 18px;\n letter-spacing: 0.2px;\n font-size: var(--font-size-1);\n border-radius: 8px;\n color: rgb(var(--palette-error));\n -webkit-animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;\n animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;\n word-wrap: break-word;\n}\n[data-supertokens~="headerTitle"] {\n font-size: var(--font-size-4);\n line-height: 40px;\n letter-spacing: 0.58px;\n font-weight: 800;\n margin-bottom: 2px;\n color: rgb(var(--palette-textTitle));\n}\n[data-supertokens~="headerSubtitle"] {\n margin-bottom: 21px;\n}\n[data-supertokens~="privacyPolicyAndTermsAndConditions"] {\n max-width: 300px;\n margin-top: 10px;\n}\n[data-supertokens~="privacyPolicyAndTermsAndConditions"] a {\n line-height: 21px;\n}\n/* TODO: split the link style into separate things*/\n/* We add this before primary and secondary text, because if they are applied to the same element the other ones take priority */\n[data-supertokens~="link"] {\n padding-left: 3px;\n padding-right: 3px;\n color: rgb(var(--palette-textLink));\n font-size: var(--font-size-1);\n cursor: pointer;\n letter-spacing: 0.16px;\n line-height: 26px;\n}\n[data-supertokens~="primaryText"] {\n font-size: var(--font-size-1);\n font-weight: 500;\n letter-spacing: 0.4px;\n line-height: 21px;\n color: rgb(var(--palette-textLabel));\n}\n[data-supertokens~="secondaryText"] {\n font-size: var(--font-size-1);\n font-weight: 300;\n letter-spacing: 0.4px;\n color: rgb(var(--palette-textPrimary));\n}\n[data-supertokens~="divider"] {\n margin-top: 1em;\n margin-bottom: 1em;\n border-bottom: 0.3px solid #dddddd;\n align-items: center;\n padding-bottom: 5px;\n}\n[data-supertokens~="headerTinyTitle"] {\n margin-top: 13px;\n font-size: var(--font-size-3);\n letter-spacing: 1.1px;\n font-weight: 500;\n line-height: 28px;\n}\n[data-supertokens~="secondaryLinkWithArrow"] {\n margin-top: 10px;\n margin-bottom: 30px;\n cursor: pointer;\n}\n[data-supertokens~="secondaryLinkWithArrow"]:hover {\n position: relative;\n left: 2px;\n word-spacing: 4px;\n}\n[data-supertokens~="generalSuccess"] {\n color: rgb(var(--palette-success));\n font-size: var(--font-size-1);\n background: rgb(var(--palette-successBackground));\n -webkit-animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;\n animation: swing-in-top-fwd 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) both;\n padding: 9px 15px 9px 15px;\n border-radius: 6px;\n display: inline-block;\n}\n[data-supertokens~="spinner"] {\n width: 80px;\n height: auto;\n padding-top: 20px;\n padding-bottom: 40px;\n margin: 0 auto;\n}\n[data-supertokens~="error"] {\n color: rgb(var(--palette-error));\n}\n[data-supertokens~="linkButton"] {\n background-color: transparent;\n border: 0;\n}\n[data-supertokens~="secondaryLinkWithLeftArrow"] {\n margin-top: 10px;\n margin-bottom: 40px;\n cursor: pointer;\n}\n[data-supertokens~="secondaryLinkWithLeftArrow"] svg {\n margin-right: 0.3em;\n}\n[data-supertokens~="secondaryLinkWithLeftArrow"]:hover svg {\n position: relative;\n left: -4px;\n}\n[data-supertokens~="button"] {\n background-color: rgb(var(--palette-primary));\n color: rgb(var(--palette-buttonText));\n width: 100%;\n height: 34px;\n font-weight: 700;\n border-width: 1px;\n border-style: solid;\n border-radius: 6px;\n border-color: rgb(var(--palette-primaryBorder));\n background-position: center;\n transition: all 0.4s;\n background-size: 12000%;\n cursor: pointer;\n}\n[data-supertokens~="button"]:disabled {\n border: none;\n cursor: no-drop;\n}\n[data-supertokens~="button"]:active {\n outline: none;\n transition: all 0s;\n background-size: 100%;\n -webkit-filter: brightness(0.85);\n filter: brightness(0.85);\n}\n[data-supertokens~="button"]:focus {\n outline: none;\n}\n[data-supertokens~="backButtonCommon"] {\n width: 16px;\n height: 13px;\n}\n[data-supertokens~="backButton"] {\n cursor: pointer;\n border: none;\n background-color: transparent;\n padding: 0px;\n}\n[data-supertokens~="backButtonPlaceholder"] {\n display: block;\n}\n/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved.\n *\n * This software is licensed under the Apache License, Version 2.0 (the\n * "License") as published by the Apache Software Foundation.\n *\n * You may not use this file except in compliance with the License. You may\n * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations\n * under the License.\n */\n[data-supertokens~="inputContainer"] {\n margin-top: 6px;\n}\n[data-supertokens~="inputWrapper"] {\n box-sizing: border-box;\n width: 100%;\n display: flex;\n align-items: center;\n background-color: rgb(var(--palette-inputBackground));\n height: 34px;\n border-radius: 6px;\n border: 1px solid rgb(var(--palette-inputBorder));\n}\n[data-supertokens~="inputWrapper"][focus-within] {\n background-color: rgba(var(--palette-inputBackground), 0.25);\n border: 1px solid rgb(var(--palette-primary));\n box-shadow: 0 0 0 0.2rem rgba(var(--palette-primary), 0.25);\n outline: none;\n}\n[data-supertokens~="inputWrapper"]:focus-within {\n background-color: rgba(var(--palette-inputBackground), 0.25);\n border: 1px solid rgb(var(--palette-primary));\n box-shadow: 0 0 0 0.2rem rgba(var(--palette-primary), 0.25);\n outline: none;\n}\n[data-supertokens~="inputError"] {\n border: 1px solid rgb(var(--palette-error));\n box-shadow: 0 0 0 0.2rem rgba(var(--palette-error), 0.25);\n outline: none;\n}\n[data-supertokens~="inputError"][focus-within] {\n border: 1px solid rgb(var(--palette-error));\n box-shadow: 0 0 0 0.2rem rgba(var(--palette-error), 0.25);\n outline: none;\n}\n[data-supertokens~="inputError"]:focus-within {\n border: 1px solid rgb(var(--palette-error));\n box-shadow: 0 0 0 0.2rem rgba(var(--palette-error), 0.25);\n outline: none;\n}\n[data-supertokens~="input"] {\n box-sizing: border-box;\n padding-left: 15px;\n -webkit-filter: none;\n filter: none;\n color: rgb(var(--palette-textInput));\n background-color: transparent;\n border-radius: 6px;\n font-size: var(--font-size-1);\n border: none;\n padding-right: 25px;\n letter-spacing: 1.2px;\n flex: 9 1 75%;\n width: 75%;\n height: 32px;\n}\n[data-supertokens~="input"]:focus {\n border: none;\n outline: none;\n}\n[data-supertokens~="input"]:-webkit-autofill,\n[data-supertokens~="input"]:-webkit-autofill:hover,\n[data-supertokens~="input"]:-webkit-autofill:focus,\n[data-supertokens~="input"]:-webkit-autofill:active {\n -webkit-text-fill-color: rgb(var(--palette-textInput));\n box-shadow: 0 0 0 30px rgb(var(--palette-inputBackground)) inset;\n}\n[data-supertokens~="inputAdornment"] {\n justify-content: center;\n margin-right: 5px;\n}\n[data-supertokens~="showPassword"] {\n cursor: pointer;\n}\n[data-supertokens~="forgotPasswordLink"] {\n margin-top: 10px;\n}\n[data-supertokens~="enterEmailSuccessMessage"] {\n margin-top: 15px;\n margin-bottom: 15px;\n word-break: break-word;\n}\n[data-supertokens~="submitNewPasswordSuccessMessage"] {\n margin-top: 15px;\n margin-bottom: 15px;\n}\n[data-supertokens~="inputErrorMessage"] {\n padding-top: 5px;\n padding-bottom: 5px;\n color: rgb(var(--palette-error));\n line-height: 24px;\n font-weight: 400;\n font-size: var(--font-size-1);\n text-align: left;\n -webkit-animation: slideTop 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;\n animation: slideTop 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;\n max-width: 330px;\n}\n@media (max-width: 440px) {\n [data-supertokens~="inputErrorMessage"] {\n max-width: 250px;\n }\n}\n[data-supertokens~="inputErrorSymbol"] {\n margin-right: 5px;\n top: 1px;\n position: relative;\n left: 2px;\n}\n[data-supertokens~="label"] {\n text-align: left;\n font-weight: 600;\n font-size: var(--font-size-1);\n line-height: 24px;\n color: rgb(var(--palette-textLabel));\n}\n[data-supertokens~="formRow"] {\n display: flex;\n flex-direction: column;\n padding-top: 0px;\n padding-bottom: 34px;\n}\n[data-supertokens~="formRow"][data-supertokens~="hasError"] {\n padding-bottom: 0;\n}\n[data-supertokens~="sendVerifyEmailIcon"] {\n margin-top: 11px;\n}\n[data-supertokens~="headerTinyTitle"] {\n margin-top: 13px;\n font-size: var(--font-size-3);\n letter-spacing: 1.1px;\n font-weight: 500;\n line-height: 28px;\n}\n[data-supertokens~="sendVerifyEmailText"] {\n line-height: 21px;\n font-size: var(--font-size-1);\n text-align: center;\n font-weight: 300;\n letter-spacing: 0.8px;\n}\n[data-supertokens~="secondaryLinkWithArrow"] {\n margin-top: 10px;\n margin-bottom: 30px;\n cursor: pointer;\n}\n[data-supertokens~="secondaryLinkWithArrow"]:hover {\n position: relative;\n left: 2px;\n word-spacing: 4px;\n}\n[data-supertokens~="sendVerifyEmailResend"] {\n margin-top: 13px;\n font-weight: 300;\n}\n[data-supertokens~="sendVerifyEmailResend"]:hover {\n text-decoration: underline;\n}\n[data-supertokens~="noFormRow"] {\n padding-bottom: 25px;\n}\n[data-supertokens~="emailVerificationButtonWrapper"] {\n padding-top: 25px;\n max-width: 96px;\n margin: 0 auto;\n}\n[data-supertokens~="withBackButton"] {\n position: relative;\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n[data-supertokens~="resendEmailLink"] {\n display: inline-block;\n}\n[data-supertokens~="generalSuccess"] {\n margin-bottom: 20px;\n -webkit-animation: swingIn 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) alternate 2 both;\n animation: swingIn 1s cubic-bezier(0.175, 0.885, 0.32, 1.275) alternate 2 both;\n}\n[data-supertokens~="codeInputLabelWrapper"] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n[data-supertokens~="headerSubtitle"] strong {\n max-width: 100%;\n display: inline-block;\n vertical-align: bottom;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n[data-supertokens~="sendCodeText"] {\n margin-top: 15px;\n margin-bottom: 20px;\n}\n[data-supertokens~="sendCodeText"] strong {\n max-width: 100%;\n display: inline-block;\n vertical-align: bottom;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n[data-supertokens~="resendCodeBtn"] {\n width: auto;\n margin-top: 0;\n line-height: 24px;\n}\n[data-supertokens~="resendCodeBtn"]:hover {\n text-decoration: underline;\n}\n[data-supertokens~="resendCodeBtn"]:disabled {\n color: rgb(var(--palette-textPrimary));\n cursor: default;\n text-decoration: none;\n}\n[data-supertokens~="phoneInputLibRoot"] {\n display: flex;\n align-items: center;\n}\n[data-supertokens~="phoneInputWrapper"] {\n display: flex;\n align-items: center;\n}\n[data-supertokens~="phoneInputWrapper"] .iti [data-supertokens~="input"] {\n padding-left: 15px;\n}\n[data-supertokens~="phoneInputWrapper"] .iti {\n flex: 1 1;\n min-width: 0;\n width: 100%;\n background: transparent;\n border: none;\n color: inherit;\n outline: none;\n}\n[data-supertokens~="continueButtonWrapper"] {\n margin-top: 10px;\n margin-bottom: 30px;\n}\n[data-supertokens~="footerLinkGroup"] {\n display: flex;\n justify-content: space-between;\n margin-top: 20px;\n gap: 8px;\n}\n[data-supertokens~="footerLinkGroup"] > div {\n margin: 0;\n}\n@media (max-width: 360px) {\n [data-supertokens~="footerLinkGroup"] {\n flex-direction: column;\n }\n [data-supertokens~="footerLinkGroup"] > div {\n margin: 0 auto;\n }\n}\n[data-supertokens~="footerLinkGroup"] div:only-child {\n margin-left: auto;\n margin-right: auto;\n}\n.iti__country-list {\n border: 0;\n top: 40px;\n width: min(72.2vw, 320px);\n border-radius: 6;\n box-shadow: 0px 0px 3px 0px rgba(0, 0, 0, 0.16);\n}\n.iti__country {\n display: flex;\n align-items: center;\n height: 34px;\n cursor: pointer;\n\n padding: 0 8px;\n}\n.iti__country-name {\n color: var(--palette-textLabel);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n margin: "0 16px";\n}\n[data-supertokens~="row"] {\n padding-bottom: 30px;\n}\n[data-supertokens~="providerContainer"] {\n padding-top: 9px;\n padding-bottom: 9px;\n}\n[data-supertokens~="providerButton"] {\n border-color: rgb(221, 221, 221) !important;\n}\n[data-supertokens~="providerButton"] {\n min-height: 32px;\n display: flex;\n flex-direction: row;\n align-items: center;\n padding: 2px 8px;\n\n background-color: white;\n color: black;\n}\n[data-supertokens~="providerButton"]:hover {\n -webkit-filter: none !important;\n filter: none !important;\n}\n[data-supertokens~="providerButton"]:hover {\n background-color: #fafafa;\n}\n[data-supertokens~="providerButtonLeft"] {\n min-width: 34px;\n margin-left: 66px;\n}\n[data-supertokens~="providerButtonLogo"] {\n height: 30px;\n display: flex;\n}\n[data-supertokens~="providerButtonLogoCenter"] {\n display: flex;\n margin: auto;\n}\n[data-supertokens~="providerButtonText"] {\n font-weight: 400;\n text-align: center;\n justify-content: center;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n display: inline-block;\n}\n[data-supertokens~="providerButtonText"]:only-child {\n margin: 0 auto;\n}\n[data-supertokens~="thirdPartyPasswordlessDivider"] {\n padding-top: 5px;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n color: rgb(var(--palette-textPrimary));\n}\n[data-supertokens~="thirdPartyPasswordlessDividerText"] {\n flex: 1 1;\n margin-top: 0.75em;\n}\n[data-supertokens~="divider"] {\n flex: 3 3;\n}\n[data-supertokens~="providerButton"] {\n margin: auto !important;\n max-width: 240px !important;\n}\n[data-supertokens~="providerButtonLeft"] {\n margin-left: 30px !important;\n}\n'; var ThemeBase = function (_a) { var children = _a.children, diff --git a/lib/ts/recipe/passwordless/components/features/mfa/index.tsx b/lib/ts/recipe/passwordless/components/features/mfa/index.tsx index 7c2d9504d..f81b4ef98 100644 --- a/lib/ts/recipe/passwordless/components/features/mfa/index.tsx +++ b/lib/ts/recipe/passwordless/components/features/mfa/index.tsx @@ -314,10 +314,10 @@ function useOnLoad( [props.recipe, userContext] ); const handleLoadError = React.useCallback( - // Test this, it may show an empty screen in many cases - () => dispatch({ type: "setError", error: "Getting mfaInfo failed!" }), + // TODO: Test this, it may show an empty screen in many cases + () => dispatch({ type: "setError", error: "SOMETHING_WENT_WRONG_ERROR" }), [dispatch] - ); // TODO: translation/proper error handling) + ); const onLoad = React.useCallback( async (mfaInfo: { factors: MFAFactorInfo; email?: string; phoneNumber?: string }) => { let error: string | undefined = undefined; @@ -356,7 +356,12 @@ function useOnLoad( userContext, }); } else if (!mfaInfo.factors.isAllowedToSetup.includes("otp-email")) { - dispatch({ type: "setError", error: "Factor not enabled" }); // TODO: translation + dispatch({ + type: "load", + loginAttemptInfo, + isAllowedToSetup: false, + error: "PWLESS_MFA_OTP_NOT_ALLOWED_TO_SETUP", + }); } else { dispatch({ type: "load", loginAttemptInfo, error, isAllowedToSetup: true }); // since loginAttemptInfo is undefined, this will ask the user for the email } @@ -368,7 +373,12 @@ function useOnLoad( userContext, }); } else if (!mfaInfo.factors.isAllowedToSetup.includes("otp-phone")) { - dispatch({ type: "setError", error: "Factor not enabled" }); // TODO: translation + dispatch({ + type: "load", + loginAttemptInfo, + isAllowedToSetup: false, + error: "PWLESS_MFA_OTP_NOT_ALLOWED_TO_SETUP", + }); } else { dispatch({ type: "load", loginAttemptInfo, error, isAllowedToSetup: true }); // since loginAttemptInfo is undefined, this will ask the user for the phone number } diff --git a/lib/ts/recipe/passwordless/components/themes/mfa/index.tsx b/lib/ts/recipe/passwordless/components/themes/mfa/index.tsx index 66ef03637..395241fb5 100644 --- a/lib/ts/recipe/passwordless/components/themes/mfa/index.tsx +++ b/lib/ts/recipe/passwordless/components/themes/mfa/index.tsx @@ -19,8 +19,10 @@ import React from "react"; import { SuperTokensBranding } from "../../../../../components/SuperTokensBranding"; import { hasFontDefined } from "../../../../../styles/styles"; +import { useTranslation } from "../../../../../translation/translationContext"; import UserContextWrapper from "../../../../../usercontext/userContextWrapper"; import GeneralError from "../../../../emailpassword/components/library/generalError"; +import { AccessDeniedScreen } from "../../../../session/prebuiltui"; import { CloseTabScreen } from "../signInUp/closeTabScreen"; import { EmailForm } from "../signInUp/emailForm"; import { PhoneForm } from "../signInUp/phoneForm"; @@ -39,6 +41,7 @@ export enum MFAScreens { EmailForm, PhoneForm, UserInputCodeForm, + AccessDenied, } /* @@ -50,6 +53,7 @@ const MFATheme: React.FC = ({ onBackButtonClicked, ...props }) => { + const t = useTranslation(); const commonProps = { recipeImplementation: props.recipeImplementation, config: props.config, @@ -60,73 +64,73 @@ const MFATheme: React.FC = ({ return activeScreen === MFAScreens.CloseTab ? ( + ) : activeScreen === MFAScreens.AccessDenied ? ( + ) : (
- {featureState.loaded && - /* TODO: this doesn't feel great */ (featureState.isSetupAllowed === true || - featureState.loginAttemptInfo !== undefined) && ( - - {activeScreen === MFAScreens.UserInputCodeForm ? ( - - props.recipeImplementation.clearLoginAttemptInfo({ - userContext: props.userContext, - }) - } - /> - ) : ( - - )} - {featureState.error !== undefined && } - {activeScreen === MFAScreens.EmailForm ? ( - - } - /> - ) : activeScreen === MFAScreens.PhoneForm ? ( - - } - /> - ) : activeScreen === MFAScreens.UserInputCodeForm ? ( - - } - /> - ) : null} - - )} + {featureState.loaded && ( + + {activeScreen === MFAScreens.UserInputCodeForm ? ( + + props.recipeImplementation.clearLoginAttemptInfo({ + userContext: props.userContext, + }) + } + /> + ) : ( + + )} + {featureState.error !== undefined && } + {activeScreen === MFAScreens.EmailForm ? ( + + } + /> + ) : activeScreen === MFAScreens.PhoneForm ? ( + + } + /> + ) : activeScreen === MFAScreens.UserInputCodeForm ? ( + + } + /> + ) : null} + + )}
@@ -147,6 +151,9 @@ function MFAThemeWrapper(props: MFAProps): JSX.Element { activeStyle = props.config.signInUpFeature.emailOrPhoneFormStyle; } else if (activeScreen === MFAScreens.PhoneForm) { activeStyle = props.config.signInUpFeature.emailOrPhoneFormStyle; + } else { + // TODO: test + activeStyle = ""; // styling the access denied screen is handled through the session recipe } return ( @@ -163,7 +170,13 @@ export default MFAThemeWrapper; export function getActiveScreen(props: Pick) { if (props.featureState.successInAnotherTab) { return MFAScreens.CloseTab; - } else if (props.featureState.loginAttemptInfo) { + } else if ( + props.featureState.error === "PWLESS_MFA_OTP_NOT_ALLOWED_TO_SETUP" || + (props.featureState.isSetupAllowed === false && props.featureState.loginAttemptInfo === undefined) + // The first condition should always be true if the second one is true, this is here as a "fallback" + ) { + return MFAScreens.AccessDenied; + } else if (props.featureState.isSetupAllowed !== true && props.featureState.loginAttemptInfo) { return MFAScreens.UserInputCodeForm; } else if (props.contactMethod === "EMAIL") { return MFAScreens.EmailForm; diff --git a/lib/ts/recipe/passwordless/components/themes/mfa/mfaFooter.tsx b/lib/ts/recipe/passwordless/components/themes/mfa/mfaFooter.tsx index a575a021d..851df151b 100644 --- a/lib/ts/recipe/passwordless/components/themes/mfa/mfaFooter.tsx +++ b/lib/ts/recipe/passwordless/components/themes/mfa/mfaFooter.tsx @@ -28,7 +28,7 @@ export const MFAFooter = withOverride( const claim = useClaimValue(MultiFactorAuthClaim); return ( - <> +
{claim.loading === false && (claim.value?.n.length ?? 0) > 1 && (
{t("PWLESS_MFA_FOOTER_LOGOUT")}
- +
); } ); diff --git a/lib/ts/recipe/passwordless/components/themes/mfa/mfaOTPFooter.tsx b/lib/ts/recipe/passwordless/components/themes/mfa/mfaOTPFooter.tsx index dcf63a9b3..1a8036146 100644 --- a/lib/ts/recipe/passwordless/components/themes/mfa/mfaOTPFooter.tsx +++ b/lib/ts/recipe/passwordless/components/themes/mfa/mfaOTPFooter.tsx @@ -12,12 +12,12 @@ * License for the specific language governing permissions and limitations * under the License. */ -import { Fragment } from "react"; - import ArrowLeftIcon from "../../../../../components/assets/arrowLeftIcon"; import { withOverride } from "../../../../../components/componentOverride/withOverride"; import { useTranslation } from "../../../../../translation/translationContext"; import { useUserContext } from "../../../../../usercontext"; +import { MultiFactorAuthClaim } from "../../../../multifactorauth"; +import { useClaimValue } from "../../../../session"; import type { MFAOTPFooterProps } from "../../../types"; @@ -27,14 +27,16 @@ export const MFAOTPFooter = withOverride( loginAttemptInfo, recipeImplementation, onSignOutClicked, + onFactorChooserButtonClicked, isSetupAllowed, }: MFAOTPFooterProps): JSX.Element { const t = useTranslation(); + const claim = useClaimValue(MultiFactorAuthClaim); const userContext = useUserContext(); return ( - - {(isSetupAllowed && ( +
+ {isSetupAllowed ? (
@@ -47,13 +49,21 @@ export const MFAOTPFooter = withOverride( ? t("PWLESS_SIGN_IN_UP_CHANGE_CONTACT_INFO_EMAIL") : t("PWLESS_SIGN_IN_UP_CHANGE_CONTACT_INFO_PHONE")}
- )) || ( -
- - {t("PWLESS_MFA_LOGOUT")} -
+ ) : ( + claim.loading === false && + (claim.value?.n.length ?? 0) > 1 && ( +
onFactorChooserButtonClicked}> + {t("PWLESS_MFA_FOOTER_CHOOSER_ANOTHER")} +
+ ) )} - +
+ + {t("PWLESS_MFA_LOGOUT")} +
+
); } ); diff --git a/lib/ts/recipe/passwordless/components/themes/styles.css b/lib/ts/recipe/passwordless/components/themes/styles.css index 379a9e767..b4a6f02c6 100644 --- a/lib/ts/recipe/passwordless/components/themes/styles.css +++ b/lib/ts/recipe/passwordless/components/themes/styles.css @@ -94,6 +94,31 @@ margin-bottom: 30px; } +[data-supertokens~="footerLinkGroup"] { + display: flex; + justify-content: space-between; + margin-top: 20px; + gap: 8px; +} + +[data-supertokens~="footerLinkGroup"] > div { + margin: 0; +} + +@media (max-width: 360px) { + [data-supertokens~="footerLinkGroup"] { + flex-direction: column; + } + [data-supertokens~="footerLinkGroup"] > div { + margin: 0 auto; + } +} + +[data-supertokens~="footerLinkGroup"] div:only-child { + margin-left: auto; + margin-right: auto; +} + .iti__country-list { border: 0; top: 40px; diff --git a/lib/ts/recipe/passwordless/components/themes/translations.ts b/lib/ts/recipe/passwordless/components/themes/translations.ts index b57509954..3885a07ba 100644 --- a/lib/ts/recipe/passwordless/components/themes/translations.ts +++ b/lib/ts/recipe/passwordless/components/themes/translations.ts @@ -76,6 +76,8 @@ export const defaultTranslationsPasswordless = { PWLESS_MFA_HEADER_TITLE_EMAIL: "Email based OTP", PWLESS_MFA_FOOTER_CHOOSER_ANOTHER: "Choose another factor", PWLESS_MFA_FOOTER_LOGOUT: "Logout", + + PWLESS_MFA_OTP_NOT_ALLOWED_TO_SETUP: "You are not allowed to set up OTP.", /* * The following are error messages from our backend SDK. * These are returned as full messages to preserver compatibilty, but they work just like the keys above. diff --git a/lib/ts/recipe/passwordless/recipe.tsx b/lib/ts/recipe/passwordless/recipe.tsx index a820d2855..0fee1d726 100644 --- a/lib/ts/recipe/passwordless/recipe.tsx +++ b/lib/ts/recipe/passwordless/recipe.tsx @@ -41,9 +41,21 @@ import type { RecipeInitResult, NormalisedConfigWithAppInfoAndRecipeID, WebJSRec import type { NormalisedAppInfo } from "../../types"; import type RecipeModule from "../recipeModule"; -/* - * Class. - */ +export const otpPhoneFactor = { + id: "otp-phone", + name: "SMS based OTP", + description: "Get an OTP code on your phone to complete the authentication request", + path: new NormalisedURLPath("/mfa/otp-phone"), + logo: OTPIcon, +}; +export const otpEmailFactor = { + id: "otp-email", + name: "Email based OTP", + description: "Get an OTP code on your email address to complete the authentication request", + path: new NormalisedURLPath("/mfa/otp-email"), + logo: OTPIcon, +}; + export default class Passwordless extends AuthRecipe< GetRedirectionURLContext, PreAndPostAPIHookAction, @@ -60,6 +72,16 @@ export default class Passwordless extends AuthRecipe< public readonly webJSRecipe: WebJSRecipeInterface = PasswordlessWebJS ) { super(config); + + PostSuperTokensInitCallbacks.addPostInitCallback(() => { + const mfa = MultiFactorAuth.getInstance(); + if (mfa !== undefined) { + mfa.addMFAFactors( + ["otp-phone", "otp-email", "link-phone", "link-email"], + [otpPhoneFactor, otpEmailFactor] + ); + } + }); } getDefaultRedirectionURL = async (context: GetRedirectionURLContext): Promise => { @@ -71,31 +93,6 @@ 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: "SMS based OTP", - description: "Get an OTP code on your phone to complete the authentication request", - path: new NormalisedURLPath("/check-auth/otp-phone"), - logo: OTPIcon, - }, - { - id: "otp-email", - name: "Email based OTP", - description: "Get an OTP code on your email address to complete the authentication request", - path: new NormalisedURLPath("/check-auth/otp-email"), - logo: OTPIcon, - }, - ] - ); - } - }); - return { recipeID: Passwordless.RECIPE_ID, authReact: (