diff --git a/CHANGELOG.md b/CHANGELOG.md index 2843d90ac..1043ee800 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add WebAuthn credential management methods: `listCredentials`, `removeCredential` - Added `createAndRegisterCredentialWithUser` method that creates and registers a credential with a user - Adds dev/release GHA pipelines +- Add `webauthn` as a secondary factor in MFA - Adds experimental support for plugins ### Breaking changes diff --git a/lib/build/components/componentOverride/genericComponentOverrideContext.d.ts b/lib/build/components/componentOverride/genericComponentOverrideContext.d.ts index 6a5174eb4..f04caefe4 100644 --- a/lib/build/components/componentOverride/genericComponentOverrideContext.d.ts +++ b/lib/build/components/componentOverride/genericComponentOverrideContext.d.ts @@ -519,6 +519,26 @@ export declare const createGenericComponentsOverrideContext: > | undefined; + WebauthnMFASignIn_Override?: + | import("./componentOverride").ComponentOverride< + React.ComponentType + > + | undefined; + WebauthnMFALoadingScreen_Override?: + | import("./componentOverride").ComponentOverride> + | undefined; + WebauthnMFASignUp_Override?: + | import("./componentOverride").ComponentOverride< + React.ComponentType + > + | undefined; + WebauthnMFASignUpConfirmation_Override?: + | import("./componentOverride").ComponentOverride< + React.ComponentType< + import("../../recipe/webauthn/components/themes/mfa/signUpConfirmation").MFASignUpConfirmationProps + > + > + | undefined; } & T), React.FC< React.PropsWithChildren<{ diff --git a/lib/build/multifactorauthprebuiltui.js b/lib/build/multifactorauthprebuiltui.js index ef1f8c7fc..e2c8d43f9 100644 --- a/lib/build/multifactorauthprebuiltui.js +++ b/lib/build/multifactorauthprebuiltui.js @@ -284,6 +284,8 @@ var defaultTranslationsMultiFactorAuth = { "Get an OTP code on your email address to complete the authentication request", TOTP_MFA_NAME: "TOTP", TOTP_MFA_DESCRIPTION: "Use an authenticator app to complete the authentication request", + WEBAUTHN_MFA_NAME: "Passkeys", + WEBAUTHN_MFA_DESCRIPTION: "Use a passkey to complete the authentication request", 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/build/recipe/authRecipe/componentOverrideContext.d.ts b/lib/build/recipe/authRecipe/componentOverrideContext.d.ts index d620478f4..32e5605a2 100644 --- a/lib/build/recipe/authRecipe/componentOverrideContext.d.ts +++ b/lib/build/recipe/authRecipe/componentOverrideContext.d.ts @@ -516,6 +516,28 @@ declare const useContext: () => }> > | undefined; + WebauthnMFASignIn_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFALoadingScreen_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFASignUp_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFASignUpConfirmation_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType< + import("../webauthn/components/themes/mfa/signUpConfirmation").MFASignUpConfirmationProps + > + > + | undefined; } & ComponentOverrideMap), Provider: import("react").FC< import("react").PropsWithChildren<{ diff --git a/lib/build/recipe/emailpassword/componentOverrideContext.d.ts b/lib/build/recipe/emailpassword/componentOverrideContext.d.ts index d3f26f67a..a199ce39f 100644 --- a/lib/build/recipe/emailpassword/componentOverrideContext.d.ts +++ b/lib/build/recipe/emailpassword/componentOverrideContext.d.ts @@ -507,6 +507,28 @@ declare const useContext: () => }> > | undefined; + WebauthnMFASignIn_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFALoadingScreen_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFASignUp_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFASignUpConfirmation_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType< + import("../webauthn/components/themes/mfa/signUpConfirmation").MFASignUpConfirmationProps + > + > + | undefined; } & ComponentOverrideMap), Provider: import("react").FC< import("react").PropsWithChildren<{ diff --git a/lib/build/recipe/emailverification/componentOverrideContext.d.ts b/lib/build/recipe/emailverification/componentOverrideContext.d.ts index cc47ac974..c182882e9 100644 --- a/lib/build/recipe/emailverification/componentOverrideContext.d.ts +++ b/lib/build/recipe/emailverification/componentOverrideContext.d.ts @@ -516,6 +516,28 @@ declare const useContext: () => }> > | undefined; + WebauthnMFASignIn_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFALoadingScreen_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFASignUp_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFASignUpConfirmation_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType< + import("../webauthn/components/themes/mfa/signUpConfirmation").MFASignUpConfirmationProps + > + > + | undefined; } & ComponentOverrideMap), Provider: import("react").FC< import("react").PropsWithChildren<{ diff --git a/lib/build/recipe/multifactorauth/componentOverrideContext.d.ts b/lib/build/recipe/multifactorauth/componentOverrideContext.d.ts index 25b1464e3..90456fb64 100644 --- a/lib/build/recipe/multifactorauth/componentOverrideContext.d.ts +++ b/lib/build/recipe/multifactorauth/componentOverrideContext.d.ts @@ -516,6 +516,28 @@ declare const useContext: () => }> > | undefined; + WebauthnMFASignIn_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFALoadingScreen_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFASignUp_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFASignUpConfirmation_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType< + import("../webauthn/components/themes/mfa/signUpConfirmation").MFASignUpConfirmationProps + > + > + | undefined; } & ComponentOverrideMap), Provider: import("react").FC< import("react").PropsWithChildren<{ diff --git a/lib/build/recipe/multifactorauth/components/themes/translations.d.ts b/lib/build/recipe/multifactorauth/components/themes/translations.d.ts index b26ba3b86..1c084aa62 100644 --- a/lib/build/recipe/multifactorauth/components/themes/translations.d.ts +++ b/lib/build/recipe/multifactorauth/components/themes/translations.d.ts @@ -8,6 +8,8 @@ export declare const defaultTranslationsMultiFactorAuth: { PWLESS_MFA_OTP_EMAIL_DESCRIPTION: string; TOTP_MFA_NAME: string; TOTP_MFA_DESCRIPTION: string; + WEBAUTHN_MFA_NAME: string; + WEBAUTHN_MFA_DESCRIPTION: string; MFA_NO_AVAILABLE_OPTIONS: string; MFA_NO_AVAILABLE_OPTIONS_LOGIN: string; AUTH_PAGE_HEADER_TITLE_SIGN_IN_AND_UP: string; diff --git a/lib/build/recipe/multifactorauth/prebuiltui.d.ts b/lib/build/recipe/multifactorauth/prebuiltui.d.ts index c7e8ab2cb..4485bcb3e 100644 --- a/lib/build/recipe/multifactorauth/prebuiltui.d.ts +++ b/lib/build/recipe/multifactorauth/prebuiltui.d.ts @@ -18,6 +18,8 @@ export declare class MultiFactorAuthPreBuiltUI extends RecipeRouter { PWLESS_MFA_OTP_EMAIL_DESCRIPTION: string; TOTP_MFA_NAME: string; TOTP_MFA_DESCRIPTION: string; + WEBAUTHN_MFA_NAME: string; + WEBAUTHN_MFA_DESCRIPTION: string; MFA_NO_AVAILABLE_OPTIONS: string; MFA_NO_AVAILABLE_OPTIONS_LOGIN: string; AUTH_PAGE_HEADER_TITLE_SIGN_IN_AND_UP: string; @@ -52,6 +54,8 @@ export declare class MultiFactorAuthPreBuiltUI extends RecipeRouter { PWLESS_MFA_OTP_EMAIL_DESCRIPTION: string; TOTP_MFA_NAME: string; TOTP_MFA_DESCRIPTION: string; + WEBAUTHN_MFA_NAME: string; + WEBAUTHN_MFA_DESCRIPTION: string; MFA_NO_AVAILABLE_OPTIONS: string; MFA_NO_AVAILABLE_OPTIONS_LOGIN: string; AUTH_PAGE_HEADER_TITLE_SIGN_IN_AND_UP: string; diff --git a/lib/build/recipe/multitenancy/componentOverrideContext.d.ts b/lib/build/recipe/multitenancy/componentOverrideContext.d.ts index f0b2436a6..0dcc165a5 100644 --- a/lib/build/recipe/multitenancy/componentOverrideContext.d.ts +++ b/lib/build/recipe/multitenancy/componentOverrideContext.d.ts @@ -516,6 +516,28 @@ declare const useContext: () => }> > | undefined; + WebauthnMFASignIn_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFALoadingScreen_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFASignUp_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFASignUpConfirmation_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType< + import("../webauthn/components/themes/mfa/signUpConfirmation").MFASignUpConfirmationProps + > + > + | undefined; } & ComponentOverrideMap), Provider: import("react").FC< import("react").PropsWithChildren<{ diff --git a/lib/build/recipe/oauth2provider/componentOverrideContext.d.ts b/lib/build/recipe/oauth2provider/componentOverrideContext.d.ts index 0445b6a8c..f0288c51d 100644 --- a/lib/build/recipe/oauth2provider/componentOverrideContext.d.ts +++ b/lib/build/recipe/oauth2provider/componentOverrideContext.d.ts @@ -516,6 +516,28 @@ declare const useContext: () => }> > | undefined; + WebauthnMFASignIn_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFALoadingScreen_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFASignUp_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFASignUpConfirmation_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType< + import("../webauthn/components/themes/mfa/signUpConfirmation").MFASignUpConfirmationProps + > + > + | undefined; } & ComponentOverrideMap), Provider: import("react").FC< import("react").PropsWithChildren<{ diff --git a/lib/build/recipe/passwordless/componentOverrideContext.d.ts b/lib/build/recipe/passwordless/componentOverrideContext.d.ts index acf4d1f90..992838f05 100644 --- a/lib/build/recipe/passwordless/componentOverrideContext.d.ts +++ b/lib/build/recipe/passwordless/componentOverrideContext.d.ts @@ -514,6 +514,28 @@ declare const useContext: () => }> > | undefined; + WebauthnMFASignIn_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFALoadingScreen_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFASignUp_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFASignUpConfirmation_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType< + import("../webauthn/components/themes/mfa/signUpConfirmation").MFASignUpConfirmationProps + > + > + | undefined; } & ComponentOverrideMap), Provider: import("react").FC< import("react").PropsWithChildren<{ diff --git a/lib/build/recipe/session/componentOverrideContext.d.ts b/lib/build/recipe/session/componentOverrideContext.d.ts index f572c5078..438337cfa 100644 --- a/lib/build/recipe/session/componentOverrideContext.d.ts +++ b/lib/build/recipe/session/componentOverrideContext.d.ts @@ -516,6 +516,28 @@ declare const useContext: () => }> > | undefined; + WebauthnMFASignIn_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFALoadingScreen_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFASignUp_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFASignUpConfirmation_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType< + import("../webauthn/components/themes/mfa/signUpConfirmation").MFASignUpConfirmationProps + > + > + | undefined; } & ComponentOverrideMap), Provider: import("react").FC< import("react").PropsWithChildren<{ diff --git a/lib/build/recipe/thirdparty/componentOverrideContext.d.ts b/lib/build/recipe/thirdparty/componentOverrideContext.d.ts index 22714cdd1..ddf36eb0f 100644 --- a/lib/build/recipe/thirdparty/componentOverrideContext.d.ts +++ b/lib/build/recipe/thirdparty/componentOverrideContext.d.ts @@ -516,6 +516,28 @@ declare const useContext: () => }> > | undefined; + WebauthnMFASignIn_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFALoadingScreen_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFASignUp_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFASignUpConfirmation_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType< + import("../webauthn/components/themes/mfa/signUpConfirmation").MFASignUpConfirmationProps + > + > + | undefined; } & ComponentOverrideMap), Provider: import("react").FC< import("react").PropsWithChildren<{ diff --git a/lib/build/recipe/totp/componentOverrideContext.d.ts b/lib/build/recipe/totp/componentOverrideContext.d.ts index 78104b5c3..7038ad4cb 100644 --- a/lib/build/recipe/totp/componentOverrideContext.d.ts +++ b/lib/build/recipe/totp/componentOverrideContext.d.ts @@ -516,6 +516,28 @@ declare const useContext: () => }> > | undefined; + WebauthnMFASignIn_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFALoadingScreen_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFASignUp_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFASignUpConfirmation_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType< + import("../webauthn/components/themes/mfa/signUpConfirmation").MFASignUpConfirmationProps + > + > + | undefined; } & ComponentOverrideMap), Provider: import("react").FC< import("react").PropsWithChildren<{ diff --git a/lib/build/recipe/webauthn/componentOverrideContext.d.ts b/lib/build/recipe/webauthn/componentOverrideContext.d.ts index 59742e794..37a93aef5 100644 --- a/lib/build/recipe/webauthn/componentOverrideContext.d.ts +++ b/lib/build/recipe/webauthn/componentOverrideContext.d.ts @@ -82,6 +82,28 @@ declare const useContext: () => }> > | undefined; + WebauthnMFASignIn_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFALoadingScreen_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFASignUp_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType + > + | undefined; + WebauthnMFASignUpConfirmation_Override?: + | import("../../components/componentOverride/componentOverride").ComponentOverride< + import("react").ComponentType< + import("./components/themes/mfa/signUpConfirmation").MFASignUpConfirmationProps + > + > + | undefined; } & ComponentOverrideMap) | ({ EmailPasswordSignInForm_Override?: diff --git a/lib/build/recipe/webauthn/components/features/mfa/index.d.ts b/lib/build/recipe/webauthn/components/features/mfa/index.d.ts new file mode 100644 index 000000000..3b5c1d136 --- /dev/null +++ b/lib/build/recipe/webauthn/components/features/mfa/index.d.ts @@ -0,0 +1,21 @@ +import * as React from "react"; +import type { FeatureBaseProps, UserContext, Navigate } from "../../../../../types"; +import type Recipe from "../../../recipe"; +import type { ComponentOverrideMap, WebAuthnMFAAction, WebAuthnMFAProps, WebAuthnMFAState } from "../../../types"; +import type { RecipeInterface } from "supertokens-web-js/recipe/webauthn"; +export declare const useFeatureReducer: () => [WebAuthnMFAState, React.Dispatch]; +export declare function useChildProps( + recipe: Recipe, + recipeImplementation: RecipeInterface, + state: WebAuthnMFAState, + dispatch: React.Dispatch, + userContext: UserContext, + navigate?: Navigate +): Omit; +export declare const MFAFeature: React.FC< + FeatureBaseProps<{ + recipe: Recipe; + useComponentOverrides: () => ComponentOverrideMap; + }> +>; +export default MFAFeature; diff --git a/lib/build/recipe/webauthn/components/themes/mfa/index.d.ts b/lib/build/recipe/webauthn/components/themes/mfa/index.d.ts new file mode 100644 index 000000000..a2fad99e2 --- /dev/null +++ b/lib/build/recipe/webauthn/components/themes/mfa/index.d.ts @@ -0,0 +1,15 @@ +/// +import { WebauthnMFALoadingScreen } from "./loadingScreen"; +import { WebauthnMFASignIn } from "./signIn"; +import { WebauthnMFASignUp } from "./signUp"; +import { WebauthnMFASignUpConfirmation } from "./signUpConfirmation"; +import type { WebAuthnMFAProps } from "../../../types"; +export { WebauthnMFALoadingScreen, WebauthnMFASignIn, WebauthnMFASignUp, WebauthnMFASignUpConfirmation }; +export declare enum MFAScreens { + SignIn = 0, + SignUp = 1, + SignUpConfirmation = 2, +} +declare function MFAThemeWrapper(props: WebAuthnMFAProps): JSX.Element; +export default MFAThemeWrapper; +export declare function MFATheme(props: WebAuthnMFAProps): JSX.Element; diff --git a/lib/build/recipe/webauthn/components/themes/mfa/loadingScreen.d.ts b/lib/build/recipe/webauthn/components/themes/mfa/loadingScreen.d.ts new file mode 100644 index 000000000..e2c43bf99 --- /dev/null +++ b/lib/build/recipe/webauthn/components/themes/mfa/loadingScreen.d.ts @@ -0,0 +1,2 @@ +/// +export declare const WebauthnMFALoadingScreen: import("react").ComponentType; diff --git a/lib/build/recipe/webauthn/components/themes/mfa/signIn.d.ts b/lib/build/recipe/webauthn/components/themes/mfa/signIn.d.ts new file mode 100644 index 000000000..3c70c5e6e --- /dev/null +++ b/lib/build/recipe/webauthn/components/themes/mfa/signIn.d.ts @@ -0,0 +1,10 @@ +import * as React from "react"; +export declare type MFASignInProps = { + onBackButtonClicked?: () => void; + onSignIn: () => Promise; + error: string | undefined; + deviceSupported: boolean; + canRegisterPasskey: boolean; + onRegisterPasskeyClick: () => void; +}; +export declare const WebauthnMFASignIn: React.ComponentType; diff --git a/lib/build/recipe/webauthn/components/themes/mfa/signUp.d.ts b/lib/build/recipe/webauthn/components/themes/mfa/signUp.d.ts new file mode 100644 index 000000000..fcba81d73 --- /dev/null +++ b/lib/build/recipe/webauthn/components/themes/mfa/signUp.d.ts @@ -0,0 +1,12 @@ +import * as React from "react"; +export declare type MFASignUpProps = { + onContinueClick: (email: string) => void; + clearError: () => void; + email?: string; + error?: string; + onError: (error: string) => void; + onFetchError?: (error: Response) => void; + onRecoverAccountClick: () => void; + onBackButtonClicked?: () => void; +}; +export declare const WebauthnMFASignUp: React.ComponentType; diff --git a/lib/build/recipe/webauthn/components/themes/mfa/signUpConfirmation.d.ts b/lib/build/recipe/webauthn/components/themes/mfa/signUpConfirmation.d.ts new file mode 100644 index 000000000..617b6b658 --- /dev/null +++ b/lib/build/recipe/webauthn/components/themes/mfa/signUpConfirmation.d.ts @@ -0,0 +1,8 @@ +import * as React from "react"; +export declare type MFASignUpConfirmationProps = { + onSignUp: () => Promise; + onBackButtonClicked?: () => void; + email: string; + error?: string; +}; +export declare const WebauthnMFASignUpConfirmation: React.ComponentType; diff --git a/lib/build/recipe/webauthn/components/themes/translations.d.ts b/lib/build/recipe/webauthn/components/themes/translations.d.ts index d1cbef610..0e5020476 100644 --- a/lib/build/recipe/webauthn/components/themes/translations.d.ts +++ b/lib/build/recipe/webauthn/components/themes/translations.d.ts @@ -38,6 +38,11 @@ export declare const defaultTranslationsWebauthn: { WEBAUTHN_NOT_SUPPORTED_ERROR: string; WEBAUTHN_PASSKEY_NOT_SUPPORTED_BY_BROWSER: string; WEBAUTHN_EMAIL_INPUT_NOT_POPULATED_ERROR: string; + WEBAUTHN_MFA_SIGN_IN_HEADER_TITLE: string; + WEBAUTHN_MFA_SIGN_IN_HEADER_SUBTITLE: string; + WEBAUTHN_MFA_DIVIDER: string; + WEBAUTHN_MFA_REGISTER_PASSKEY_SUBTITLE: string; + WEBAUTHN_MFA_REGISTER_PASSKEY_TITLE: string; AUTH_PAGE_HEADER_TITLE_SIGN_IN_AND_UP: string; AUTH_PAGE_HEADER_TITLE_SIGN_IN: string; AUTH_PAGE_HEADER_TITLE_SIGN_UP: string; diff --git a/lib/build/recipe/webauthn/constants.d.ts b/lib/build/recipe/webauthn/constants.d.ts index b14d90df5..5d814f2de 100644 --- a/lib/build/recipe/webauthn/constants.d.ts +++ b/lib/build/recipe/webauthn/constants.d.ts @@ -1,2 +1,3 @@ export declare const DEFAULT_WEBAUTHN_RECOVERY_PATH = "/webauthn/recover"; export declare const DEFAULT_WEBAUTHN_SEND_RECOVERY_EMAIL_PATH = "/webauthn/recover/send-email"; +export declare const DEFAULT_WEBAUTHN_MFA_PATH = "/mfa/webauthn"; diff --git a/lib/build/recipe/webauthn/index.d.ts b/lib/build/recipe/webauthn/index.d.ts index e97066873..4d4a0430f 100644 --- a/lib/build/recipe/webauthn/index.d.ts +++ b/lib/build/recipe/webauthn/index.d.ts @@ -94,6 +94,7 @@ export default class Wrapper { static signUp(input: { webauthnGeneratedOptionsId: string; credential: RegistrationResponseJSON; + shouldTryLinkingWithSessionUser?: boolean; options?: RecipeFunctionOptions; userContext: any; }): Promise< @@ -133,6 +134,7 @@ export default class Wrapper { static signIn(input: { webauthnGeneratedOptionsId: string; credential: AuthenticationResponseJSON; + shouldTryLinkingWithSessionUser?: boolean; options?: RecipeFunctionOptions; userContext: any; }): Promise< @@ -251,6 +253,7 @@ export default class Wrapper { >; static registerCredentialWithSignUp(input: { email: string; + shouldTryLinkingWithSessionUser?: boolean; options?: RecipeFunctionOptions; userContext: any; }): Promise< @@ -307,7 +310,11 @@ export default class Wrapper { error: any; } >; - static authenticateCredentialWithSignIn(input: { options?: RecipeFunctionOptions; userContext: any }): Promise< + static authenticateCredentialWithSignIn(input: { + shouldTryLinkingWithSessionUser?: boolean; + options?: RecipeFunctionOptions; + userContext: any; + }): Promise< | { status: "OK"; user: User; diff --git a/lib/build/recipe/webauthn/prebuiltui.d.ts b/lib/build/recipe/webauthn/prebuiltui.d.ts index 4dbe28cb3..c00088bae 100644 --- a/lib/build/recipe/webauthn/prebuiltui.d.ts +++ b/lib/build/recipe/webauthn/prebuiltui.d.ts @@ -46,6 +46,11 @@ export declare class WebauthnPreBuiltUI extends RecipeRouter { WEBAUTHN_NOT_SUPPORTED_ERROR: string; WEBAUTHN_PASSKEY_NOT_SUPPORTED_BY_BROWSER: string; WEBAUTHN_EMAIL_INPUT_NOT_POPULATED_ERROR: string; + WEBAUTHN_MFA_SIGN_IN_HEADER_TITLE: string; + WEBAUTHN_MFA_SIGN_IN_HEADER_SUBTITLE: string; + WEBAUTHN_MFA_DIVIDER: string; + WEBAUTHN_MFA_REGISTER_PASSKEY_SUBTITLE: string; + WEBAUTHN_MFA_REGISTER_PASSKEY_TITLE: string; AUTH_PAGE_HEADER_TITLE_SIGN_IN_AND_UP: string; AUTH_PAGE_HEADER_TITLE_SIGN_IN: string; AUTH_PAGE_HEADER_TITLE_SIGN_UP: string; @@ -80,7 +85,7 @@ export declare class WebauthnPreBuiltUI extends RecipeRouter { ): JSX.Element; getFeatures: (useComponentOverrides?: () => GenericComponentOverrideMap) => RecipeFeatureComponentMap; getFeatureComponent: ( - componentName: "webauthn-recover-account" | "webauthn-send-recovery-email", + componentName: "webauthn-recover-account" | "webauthn-send-recovery-email" | "webauthn-mfa", props: FeatureBaseProps<{ userContext?: UserContext; }>, diff --git a/lib/build/recipe/webauthn/recipe.d.ts b/lib/build/recipe/webauthn/recipe.d.ts index 552617183..41a7c79d0 100644 --- a/lib/build/recipe/webauthn/recipe.d.ts +++ b/lib/build/recipe/webauthn/recipe.d.ts @@ -1,4 +1,5 @@ import WebauthnWebJS from "supertokens-web-js/lib/build/recipe/webauthn"; +import PasskeyIcon from "../../components/assets/passkeyIcon"; import AuthRecipe from "../authRecipe"; import type { GetRedirectionURLContext, @@ -8,6 +9,13 @@ import type { UserInput, } from "./types"; import type { NormalisedConfigWithAppInfoAndRecipeID, WebJSRecipeInterface, RecipeInitResult } from "../../types"; +export declare const webauthnFactor: { + id: "webauthn"; + name: string; + description: string; + path: string; + logo: typeof PasskeyIcon; +}; export default class Webauthn extends AuthRecipe< GetRedirectionURLContext, PreAndPostAPIHookAction, diff --git a/lib/build/recipe/webauthn/types.d.ts b/lib/build/recipe/webauthn/types.d.ts index cace33386..660070af1 100644 --- a/lib/build/recipe/webauthn/types.d.ts +++ b/lib/build/recipe/webauthn/types.d.ts @@ -1,4 +1,3 @@ -/// import type { PasskeyNotSupportedError } from "./components/themes/error/passkeyNotSupportedError"; import type Recipe from "./recipe"; import type { ComponentOverride } from "../../components/componentOverride/componentOverride"; @@ -15,6 +14,12 @@ import type { Config as AuthRecipeModuleConfig, } from "../authRecipe/types"; import type { ContinueWithPasskeyWithOverride } from "./components/themes/continueWithPasskey"; +import type { + WebauthnMFASignIn, + WebauthnMFALoadingScreen, + WebauthnMFASignUp, + WebauthnMFASignUpConfirmation, +} from "./components/themes/mfa"; import type { PasskeyRecoveryEmailSent } from "./components/themes/sendRecoveryEmail/emailSent"; import type { WebauthnRecoverAccount, @@ -25,6 +30,8 @@ import type { ContinueWithoutPasskey } from "./components/themes/signUp/continue import type { PasskeyFeatureBlock } from "./components/themes/signUp/featureBlocks"; import type { SignUpFormInner } from "./components/themes/signUp/signUpForm"; import type { SignUpSomethingWentWrong } from "./components/themes/signUp/somethingWentWrong"; +import type { Dispatch } from "react"; +import type React from "react"; import type { RecipeInterface } from "supertokens-web-js/recipe/webauthn"; import type WebJSRecipe from "supertokens-web-js/recipe/webauthn"; import type { User } from "supertokens-web-js/types"; @@ -96,6 +103,10 @@ export declare type ComponentOverrideMap = { WebauthnContinueWithoutPasskey_Override?: ComponentOverride; WebauthnPasskeySignUpForm_Override?: ComponentOverride; WebauthnPasskeySignUpSomethingWentWrong_Override?: ComponentOverride; + WebauthnMFASignIn_Override?: ComponentOverride; + WebauthnMFALoadingScreen_Override?: ComponentOverride; + WebauthnMFASignUp_Override?: ComponentOverride; + WebauthnMFASignUpConfirmation_Override?: ComponentOverride; }; export declare type SignUpThemeBaseProps = { clearError: () => void; @@ -206,3 +217,52 @@ export declare type EmailSentProps = { email: string; onEmailChangeClick: () => void; }; +export declare type WebAuthnMFAAction = + | { + type: "setError"; + accessDenied?: boolean; + error: string | undefined; + } + | { + type: "load"; + deviceSupported: boolean; + error: string | undefined; + email: string | undefined; + showBackButton: boolean; + canRegisterPasskey: boolean; + hasRegisteredPassKey: boolean; + }; +declare type WebAuthnMFAInitialState = { + error: undefined; + loaded: false; + accessDenied: boolean; + deviceSupported: boolean; + showBackButton: boolean; + canRegisterPasskey: false; + hasRegisteredPassKey: false; + email: undefined; +}; +declare type WebAuthnMFALoadedState = { + error: string | undefined; + loaded: true; + accessDenied: boolean; + deviceSupported: boolean; + showBackButton: boolean; + canRegisterPasskey: boolean; + hasRegisteredPassKey: boolean; + email: string | undefined; +}; +export declare type WebAuthnMFAState = WebAuthnMFAInitialState | WebAuthnMFALoadedState; +export declare type WebAuthnMFAProps = { + recipeImplementation: RecipeImplementation; + config: NormalisedConfig; + onBackButtonClicked: () => void; + onSignOutClicked: () => void; + onSignIn: () => Promise; + onSignUp: (email: string) => Promise; + onRecoverAccountClick: () => void; + userContext?: UserContext; + featureState: WebAuthnMFAState; + dispatch: Dispatch; +}; +export {}; diff --git a/lib/build/webauthn-shared.js b/lib/build/webauthn-shared.js index e3f105cd8..655d23347 100644 --- a/lib/build/webauthn-shared.js +++ b/lib/build/webauthn-shared.js @@ -2,7 +2,10 @@ var genericComponentOverrideContext = require("./genericComponentOverrideContext.js"); var WebauthnWebJS = require("supertokens-web-js/lib/build/recipe/webauthn"); +var postSuperTokensInitCallbacks = require("supertokens-web-js/utils/postSuperTokensInitCallbacks"); +var jsxRuntime = require("react/jsx-runtime"); var index = require("./authRecipe-shared2.js"); +var recipe = require("./multifactorauth-shared2.js"); var types = require("./multifactorauth-shared.js"); var utils = require("./authRecipe-shared.js"); @@ -12,6 +15,37 @@ function _interopDefault(e) { var WebauthnWebJS__default = /*#__PURE__*/ _interopDefault(WebauthnWebJS); +/* Copyright (c) 2025, 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. + */ +function PasskeyIcon() { + return jsxRuntime.jsx( + "svg", + genericComponentOverrideContext.__assign( + { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, + { + children: jsxRuntime.jsx("path", { + fillRule: "evenodd", + clipRule: "evenodd", + d: "M5.23974 0.00426122C5.20971 0.00917398 5.11635 0.023531 5.03227 0.0361517C4.26872 0.150797 3.53667 0.527026 2.99234 1.08462C1.7778 2.32876 1.67594 4.23877 2.75137 5.60384C3.47855 6.52688 4.6728 7.04702 5.85871 6.9572C6.17441 6.93329 6.23003 6.92444 6.5173 6.85235C7.49851 6.60612 8.30571 5.98307 8.79333 5.09562C9.19012 4.37346 9.30287 3.48404 9.1018 2.66238C8.85299 1.64555 8.10397 0.756337 7.12878 0.320054C6.63453 0.0989376 6.24276 0.0133032 5.67651 0.00256712C5.46631 -0.00143509 5.26976 -0.000672722 5.23974 0.00426122ZM10.6557 5.27343C10.0527 5.37376 9.55624 5.62311 9.14615 6.03156C8.75205 6.42412 8.50113 6.87791 8.36981 7.43553C8.33814 7.56998 8.33022 7.69015 8.33135 8.01787C8.33214 8.24497 8.34291 8.46891 8.35529 8.51549L8.37778 8.6002L7.45649 9.50016C6.69978 10.2393 6.51909 10.4271 6.44506 10.5509C6.14071 11.0599 6.15283 11.626 6.47843 12.1093C6.65032 12.3645 6.88539 12.5333 7.22706 12.6471C7.41015 12.7081 7.882 12.7037 8.08645 12.6391C8.4564 12.5223 8.46699 12.5139 9.49199 11.5255L10.4357 10.6155L10.6931 10.6508C11.2021 10.7205 11.6431 10.6746 12.1298 10.5012C12.6661 10.3101 13.0696 10.0101 13.4499 9.51964C13.7248 9.16523 13.8805 8.83599 13.9642 8.43238C14.0168 8.17874 14.0105 7.6749 13.9514 7.41044C13.7089 6.3251 12.8932 5.543 11.7477 5.29734C11.5289 5.25043 10.8794 5.2362 10.6557 5.27343ZM11.3837 7.50376C11.6039 7.58029 11.7631 7.85197 11.707 8.05537C11.6436 8.28527 11.4241 8.46336 11.2057 8.46209C11.1131 8.46156 10.9524 8.39644 10.877 8.32889C10.6068 8.08705 10.6618 7.67956 10.9856 7.5231C11.1211 7.45762 11.235 7.45209 11.3837 7.50376ZM4.91216 7.91218C4.22601 8.00179 3.70343 8.15612 3.11079 8.44415C1.63725 9.16034 0.531334 10.5122 0.15953 12.0518C0.0539832 12.4889 -0.0159007 13.1132 0.0031208 13.4491C0.0172068 13.6976 0.126772 13.8739 0.322949 13.9636C0.397419 13.9977 0.734237 14 5.63283 14H10.8632L10.952 13.9539C11.078 13.8885 11.1898 13.7597 11.2349 13.6282C11.2674 13.5334 11.2714 13.4709 11.2602 13.2388C11.239 12.8009 11.1878 12.4787 11.0683 12.0307C11.0094 11.8095 10.9775 11.7616 10.895 11.77C10.8442 11.7752 10.6791 11.9233 10.0829 12.4983C9.35956 13.1959 9.19625 13.338 8.96323 13.4727C8.39003 13.8042 7.595 13.8892 6.92132 13.6911C6.71145 13.6294 6.36331 13.458 6.19271 13.3324C5.60763 12.9016 5.24956 12.2772 5.19726 11.5966C5.15424 11.0364 5.27535 10.5421 5.56898 10.0796C5.67022 9.92014 5.80553 9.77737 6.50092 9.0963C7.2706 8.34249 7.31441 8.29544 7.31441 8.22274C7.31441 8.12885 7.27373 8.1002 7.07419 8.05359C6.30527 7.874 5.57177 7.82603 4.91216 7.91218Z", + fill: "currentColor", + }), + } + ) + ); +} + /* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. * * This software is licensed under the Apache License, Version 2.0 (the @@ -28,6 +62,7 @@ var WebauthnWebJS__default = /*#__PURE__*/ _interopDefault(WebauthnWebJS); */ var DEFAULT_WEBAUTHN_RECOVERY_PATH = "/webauthn/recover"; var DEFAULT_WEBAUTHN_SEND_RECOVERY_EMAIL_PATH = "/webauthn/recover/send-email"; +var DEFAULT_WEBAUTHN_MFA_PATH = "/mfa/webauthn"; var getFunctionOverrides = function (onHandleEvent) { return function (originalImp) { @@ -155,6 +190,13 @@ function normalisePasskeyBaseConfig(config) { * License for the specific language governing permissions and limitations * under the License. */ +var webauthnFactor = { + id: types.FactorIds.WEBAUTHN, + name: "WEBAUTHN_MFA_NAME", + description: "WEBAUTHN_MFA_DESCRIPTION", + path: "/mfa/webauthn", + logo: PasskeyIcon, +}; var Webauthn = /** @class */ (function (_super) { genericComponentOverrideContext.__extends(Webauthn, _super); function Webauthn(config, webJSRecipe) { @@ -182,9 +224,13 @@ var Webauthn = /** @class */ (function (_super) { }); }); }; + postSuperTokensInitCallbacks.PostSuperTokensInitCallbacks.addPostInitCallback(function () { + var mfa = recipe.MultiFactorAuth.getInstance(); + if (mfa !== undefined) { + mfa.addMFAFactors([webauthnFactor]); + } + }); return _this; - // We can ideally call postInitCallbacks to set MFA's if - // we are using it. } Webauthn.prototype.getFirstFactorsForAuthPage = function () { return this.firstFactorIds; @@ -248,8 +294,10 @@ var _a = genericComponentOverrideContext.createGenericComponentsOverrideContext( useContext = _a[0], Provider = _a[1]; +exports.DEFAULT_WEBAUTHN_MFA_PATH = DEFAULT_WEBAUTHN_MFA_PATH; exports.DEFAULT_WEBAUTHN_RECOVERY_PATH = DEFAULT_WEBAUTHN_RECOVERY_PATH; exports.DEFAULT_WEBAUTHN_SEND_RECOVERY_EMAIL_PATH = DEFAULT_WEBAUTHN_SEND_RECOVERY_EMAIL_PATH; +exports.PasskeyIcon = PasskeyIcon; exports.Provider = Provider; exports.Webauthn = Webauthn; exports.useContext = useContext; diff --git a/lib/build/webauthn.js b/lib/build/webauthn.js index ce1671cc0..c64cd9c87 100644 --- a/lib/build/webauthn.js +++ b/lib/build/webauthn.js @@ -22,6 +22,9 @@ require("./multifactorauth-shared.js"); require("supertokens-web-js/recipe/session"); require("./oauth2provider-shared.js"); require("supertokens-web-js/recipe/oauth2provider"); +require("./multifactorauth-shared2.js"); +require("supertokens-web-js/recipe/multifactorauth"); +require("supertokens-web-js/utils/sessionClaimValidatorStore"); require("./authRecipe-shared.js"); /* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. diff --git a/lib/build/webauthnprebuiltui.js b/lib/build/webauthnprebuiltui.js index a477cb830..4fc886ceb 100644 --- a/lib/build/webauthnprebuiltui.js +++ b/lib/build/webauthnprebuiltui.js @@ -7,38 +7,40 @@ var uiEntry = require("./index2.js"); require("./multifactorauth.js"); var componentOverrideContext = require("./webauthn-shared.js"); var React = require("react"); +var windowHandler = require("supertokens-web-js/utils/windowHandler"); +var recipe = require("./multifactorauth-shared2.js"); +var types = require("./multifactorauth-shared.js"); var translationContext = require("./translationContext.js"); +var sessionprebuiltui = require("./sessionprebuiltui.js"); var button = require("./emailpassword-shared.js"); -var STGeneralError = require("supertokens-web-js/lib/build/error"); +var STGeneralError = require("supertokens-web-js/utils/error"); var formBase = require("./emailpassword-shared6.js"); var validators = require("./emailpassword-shared5.js"); +var STGeneralError$1 = require("supertokens-web-js/lib/build/error"); var authCompWrapper = require("./authCompWrapper.js"); var session = require("./session.js"); -var types = require("./multifactorauth-shared.js"); -var STGeneralError$1 = require("supertokens-web-js/utils/error"); require("supertokens-web-js"); require("supertokens-web-js/utils/cookieHandler"); require("supertokens-web-js/utils/postSuperTokensInitCallbacks"); -require("supertokens-web-js/utils/windowHandler"); require("supertokens-web-js/recipe/multitenancy"); require("supertokens-web-js/utils"); require("supertokens-web-js/utils/normalisedURLDomain"); require("supertokens-web-js/utils/normalisedURLPath"); require("react-dom"); require("./multitenancy-shared.js"); -require("./multifactorauth-shared2.js"); -require("supertokens-web-js/recipe/multifactorauth"); -require("supertokens-web-js/utils/sessionClaimValidatorStore"); -require("./recipeModule-shared.js"); require("./oauth2provider-shared.js"); require("supertokens-web-js/recipe/oauth2provider"); +require("./recipeModule-shared.js"); require("./authRecipe-shared.js"); require("./multifactorauth-shared3.js"); require("supertokens-web-js/lib/build/recipe/webauthn"); require("./authRecipe-shared2.js"); -require("./emailpassword-shared4.js"); +require("supertokens-web-js/recipe/multifactorauth"); +require("supertokens-web-js/utils/sessionClaimValidatorStore"); require("supertokens-web-js/recipe/session"); require("./session-shared.js"); +require("./arrowLeftIcon.js"); +require("./emailpassword-shared4.js"); function _interopDefault(e) { return e && e.__esModule ? e : { default: e }; @@ -75,30 +77,42 @@ var React__namespace = /*#__PURE__*/ _interopNamespace(React); var STGeneralError__default = /*#__PURE__*/ _interopDefault(STGeneralError); var STGeneralError__default$1 = /*#__PURE__*/ _interopDefault(STGeneralError$1); -/* Copyright (c) 2021, VRAI Labs and/or its affiliates. All rights reserved. - * - * This software is licensed under the Apache License, Version 2.0 (the - * "License") as published by the Apache Software Foundation. - * - * You may not use this file except in compliance with the License. You may - * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -var RecoverAccountScreen; -(function (RecoverAccountScreen) { - RecoverAccountScreen[(RecoverAccountScreen["ContinueWithPasskey"] = 0)] = "ContinueWithPasskey"; - RecoverAccountScreen[(RecoverAccountScreen["Success"] = 1)] = "Success"; -})(RecoverAccountScreen || (RecoverAccountScreen = {})); -var SendRecoveryEmailScreen; -(function (SendRecoveryEmailScreen) { - SendRecoveryEmailScreen[(SendRecoveryEmailScreen["RecoverAccount"] = 0)] = "RecoverAccount"; - SendRecoveryEmailScreen[(SendRecoveryEmailScreen["RecoverEmailSent"] = 1)] = "RecoverEmailSent"; -})(SendRecoveryEmailScreen || (SendRecoveryEmailScreen = {})); +var styles = + '[data-supertokens~="container"] {\n --palette-background: 255, 255, 255;\n --palette-inputBackground: 250, 250, 250;\n --palette-inputBorder: 224, 224, 224;\n --palette-primary: 28, 34, 42;\n --palette-primaryBorder: 45, 54, 68;\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: 0, 0, 0;\n --palette-textLabel: 0, 0, 0;\n --palette-textInput: 0, 0, 0;\n --palette-textPrimary: 128, 128, 128;\n --palette-textLink: 0, 122, 255;\n --palette-buttonText: 255, 255, 255;\n --palette-textGray: 54, 54, 54;\n --palette-superTokensBrandingBackground: 242, 245, 246;\n --palette-superTokensBrandingText: 173, 189, 196;\n --palette-buttonGreyedOut: 221, 221, 221;\n --palette-caution: 124, 96, 62;\n --palette-errorDark: 207, 54, 68;\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 --font-size-5: 28px;\n}\n/*\n * Default styles.\n */\n@keyframes slideTop {\n 0% {\n transform: translateY(-5px);\n }\n 100% {\n transform: translateY(0px);\n }\n}\n@keyframes swing-in-top-fwd {\n 0% {\n transform: rotateX(-100deg);\n transform-origin: top;\n opacity: 0;\n }\n 100% {\n transform: rotateX(0deg);\n transform-origin: top;\n opacity: 1;\n }\n}\n[data-supertokens~="container"] {\n font-family: "Arial", 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: 10px auto 0;\n background: rgb(var(--palette-superTokensBrandingBackground));\n color: rgb(var(--palette-superTokensBrandingText));\n text-decoration: none;\n width: -webkit-fit-content;\n width: fit-content;\n border-radius: 6px 6px 0 0;\n padding: 4px 9px;\n font-weight: 400;\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: 10px;\n margin-top: 24px;\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 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: 27.6px;\n letter-spacing: 0.58px;\n font-weight: 700;\n margin-bottom: 20px;\n color: rgb(var(--palette-textTitle));\n}\n[data-supertokens~="headerSubtitle"] {\n font-weight: 400;\n color: rgb(var(--palette-textGray));\n margin-bottom: 21px;\n}\n[data-supertokens~="headerSubtitle"][data-supertokens~="secondaryText"] {\n color: rgb(var(--palette-textGray));\n font-weight: 400;\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-2);\n font-weight: 400;\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: 400;\n letter-spacing: 0.4px;\n color: rgb(var(--palette-textPrimary));\n}\n[data-supertokens~="secondaryText"] strong {\n font-weight: 600;\n}\n[data-supertokens~="divider"] {\n margin-top: 1.5em;\n margin-bottom: 1.5em;\n border-bottom: 0.3px solid #dddddd;\n align-items: center;\n padding-bottom: 5px;\n flex: 3 3;\n}\n[data-supertokens~="headerTinyTitle"] {\n margin-top: 24px;\n font-size: var(--font-size-5);\n letter-spacing: 1.1px;\n font-weight: 700;\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 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 font-family: "Arial", sans-serif;\n background-color: transparent;\n border: 0;\n}\n[data-supertokens~="secondaryLinkWithLeftArrow"] {\n color: rgb(var(--palette-textGray));\n font-weight: 400;\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 font-family: "Arial", sans-serif;\n background-color: rgb(var(--palette-primary));\n color: rgb(var(--palette-buttonText));\n width: 100%;\n height: 34px;\n font-weight: 600;\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~="buttonGreyedOut"] {\n background-color: rgb(var(--palette-buttonGreyedOut));\n border-color: rgb(var(--palette-buttonGreyedOut));\n}\n[data-supertokens~="buttonWithIcon"] {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\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 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[data-supertokens~="delayedRender"] {\n animation-duration: 0.1s;\n animation-name: animate-fade;\n animation-delay: 0.2s;\n animation-fill-mode: backwards;\n}\n@keyframes animate-fade {\n 0% {\n opacity: 0;\n }\n 100% {\n opacity: 1;\n }\n}\n[data-supertokens~="footerLinkGroupVert"] {\n display: flex;\n flex-direction: column;\n margin-top: 10px;\n gap: 24px;\n}\n[data-supertokens~="footerLinkGroupVert"] > div {\n cursor: pointer;\n margin: 0;\n}\n[data-supertokens~="footerLinkGroupVert"] [data-supertokens~="secondaryText"] {\n font-weight: 400;\n}\n[data-supertokens~="footerLinkGroupVert"] [data-supertokens~="secondaryLinkWithLeftArrow"] {\n font-weight: 400;\n position: relative;\n left: -6px; /* half the width of the left arrow */\n}\n@media (max-width: 360px) {\n [data-supertokens~="footerLinkGroupVert"] {\n flex-direction: column;\n }\n [data-supertokens~="footerLinkGroupVert"] > div {\n margin: 0 auto;\n }\n}\n[data-supertokens~="footerLinkGroupVert"] div:only-child {\n margin-left: auto;\n margin-right: auto;\n margin-top: 14px;\n}\n[data-supertokens~="withBackButton"] {\n position: relative;\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n[data-supertokens~="dividerWithOr"] {\n padding-top: 5px;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n color: rgb(var(--palette-textPrimary));\n}\n[data-supertokens~="dividerText"] {\n flex: 1 1;\n font-weight: 400;\n font-size: var(--font-size-1);\n}\n[data-supertokens~="formLabelWithLinkWrapper"] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n[data-supertokens~="formLabelLinkBtn"] {\n width: auto;\n margin-top: 0;\n line-height: 24px;\n font-size: var(--font-size-0);\n}\n[data-supertokens~="formLabelLinkBtn"]:hover {\n text-decoration: underline;\n}\n[data-supertokens~="formLabelLinkBtn"]:disabled {\n color: rgb(var(--palette-textPrimary));\n cursor: default;\n text-decoration: none;\n}\n[data-supertokens~="authComponentList"] {\n padding-bottom: 20px;\n}\n[data-supertokens~="authPageTitleOAuthClient"] {\n color: rgb(var(--palette-textGray));\n font-size: var(--font-size-1);\n font-weight: 400;\n margin: 10px 0 25px;\n}\n[data-supertokens~="authPageTitleOAuthClientUrl"] {\n text-decoration: none;\n}\n[data-supertokens~="authPageTitleOAuthClientLogo"] {\n width: 44px;\n height: 44px;\n margin-bottom: 10px;\n}\n[data-supertokens~="authPageTitleOAuthClient"] [data-supertokens~="authPageTitleOAuthClientName"] {\n color: rgb(var(--palette-textTitle));\n}\n[data-supertokens~="buttonWithArrow"] {\n border-radius: 6px;\n border: 1px solid #d0d5dd;\n width: 100%;\n color: rgb(var(--palette-textGray));\n display: flex;\n justify-content: center;\n align-items: center;\n gap: 5px;\n margin: 24px 0;\n min-height: 48px;\n cursor: pointer;\n}\n[data-supertokens~="buttonWithArrow"]:hover {\n background-color: rgb(var(--palette-inputBackground));\n}\n[data-supertokens~="buttonWithArrow"] [data-supertokens~="secondaryText"] {\n font-weight: 700;\n font-size: var(--font-size-2);\n color: rgb(var(--palette-textGray));\n margin: 0;\n}\n[data-supertokens~="buttonWithArrow"]:hover [data-supertokens~="secondaryLinkWithRightArrow"] ~ svg {\n position: relative;\n left: 2px;\n}\n[data-supertokens~="buttonWithArrow"]:hover [data-supertokens~="secondaryLinkWithLeftArrow"] svg {\n position: relative;\n left: -2px;\n}\n[data-supertokens~="buttonWithArrow"] [data-supertokens~="secondaryLinkWithLeftArrow"] {\n display: flex;\n align-items: center;\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 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~="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 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: 700;\n font-size: var(--font-size-0);\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: 20px;\n}\n[data-supertokens~="formRow"][data-supertokens~="hasError"] {\n padding-bottom: 0;\n}\n[data-supertokens~="formRow"]:last-child {\n padding-bottom: 0;\n}\n[data-supertokens~="sendVerifyEmailIcon"] {\n margin-top: 11px;\n}\n[data-supertokens~="primaryText"][data-supertokens~="sendVerifyEmailText"] {\n text-align: center;\n letter-spacing: 0.8px;\n color: rgb(var(--palette-textPrimary));\n}\n[data-supertokens~="secondaryLinkWithArrow"] {\n margin-top: 10px;\n margin-bottom: 30px;\n cursor: pointer;\n font-weight: 700;\n}\n[data-supertokens~="sendVerifyEmailResend"] {\n margin-top: 13px;\n font-weight: 400;\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~="resendEmailLink"] {\n display: inline-block;\n}\n[data-supertokens~="resetPasswordEmailForm"] {\n padding-bottom: 20px;\n}\n[data-supertokens~="resetPasswordPasswordForm"] {\n padding-bottom: 20px;\n}\n[data-supertokens~="webauthn"] [data-supertokens~="container"] {\n padding-top: 24px;\n}\n[data-supertokens~="continueWithPasskeyButtonWrapper"] {\n margin: 9px 0;\n}\n[data-supertokens~="continueWithPasskeyButtonNotSupported"] {\n margin-top: 10px;\n padding: 10px;\n border-radius: 6px;\n background-color: rgba(0,122,255,0.10196);\n color: rgb(var(--palette-textLink));\n text-align: center;\n font-weight: 400;\n font-size: var(--font-size-1);\n line-height: 16.1px;\n letter-spacing: 0%;\n text-align: center;\n}\n[data-supertokens~="continueWithoutPasskey"] {\n margin-top: 15px;\n}\n[data-supertokens~="continueWithoutPasskey"] [data-supertokens~="continueWithoutPasskeyLabel"] {\n font-size: var(--font-size-1);\n font-weight: 700;\n line-height: 21px;\n text-align: center;\n text-underline-position: from-font;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n color: rgb(var(--palette-textLink));\n cursor: pointer;\n}\n[data-supertokens~="signUpFormInnerContainer"] [data-supertokens~="cautionMessage"] {\n padding: 14px;\n background: rgba(255,149,0,0.10196);\n color: rgb(var(--palette-caution));\n font-size: var(--font-size-1);\n font-weight: 400;\n line-height: 20px;\n text-align: center;\n text-underline-position: from-font;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n border-radius: 6px;\n margin-bottom: 14px;\n}\n[data-supertokens~="passkeyConfirmationContainer"] [data-supertokens~="passkeyConfirmationEmailContainer"] {\n padding-bottom: 5px;\n}\n[data-supertokens~="passkeyConfirmationContainer"]\n [data-supertokens~="passkeyConfirmationEmailContainer"]\n [data-supertokens~="continueWithLabel"] {\n font-size: var(--font-size-0);\n font-weight: 400;\n line-height: 13.8px;\n text-align: center;\n text-underline-position: from-font;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n color: rgb(var(--palette-textGray));\n}\n[data-supertokens~="passkeyConfirmationContainer"]\n [data-supertokens~="passkeyConfirmationEmailContainer"]\n [data-supertokens~="enteredEmailId"] {\n font-size: var(--font-size-1);\n font-weight: 700;\n line-height: 16.1px;\n text-align: center;\n text-underline-position: from-font;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n}\n[data-supertokens~="passkeyConfirmationContainer"]\n [data-supertokens~="passkeyFeatureBlocksContainer"]\n [data-supertokens~="passkeyFeatureBlock"] {\n margin-top: 10px;\n padding: 18px;\n border-radius: 6px;\n border: 1px solid #dddddd;\n display: flex;\n}\n[data-supertokens~="passkeyConfirmationContainer"]\n [data-supertokens~="passkeyFeatureBlocksContainer"]\n [data-supertokens~="passkeyFeatureBlock"]\n [data-supertokens~="passkeyFeatureBlockDetails"] {\n margin-left: 18px;\n}\n[data-supertokens~="passkeyConfirmationContainer"]\n [data-supertokens~="passkeyFeatureBlocksContainer"]\n [data-supertokens~="passkeyFeatureBlock"]\n [data-supertokens~="passkeyFeatureBlockDetails"]\n [data-supertokens~="passkeyFeatureBlockTitle"] {\n font-size: var(--font-size-1);\n font-weight: 700;\n line-height: 16.1px;\n text-align: left;\n text-underline-position: from-font;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n color: rgb(var(--palette-textTitle));\n}\n[data-supertokens~="passkeyConfirmationContainer"]\n [data-supertokens~="passkeyFeatureBlocksContainer"]\n [data-supertokens~="passkeyFeatureBlock"]\n [data-supertokens~="passkeyFeatureBlockDetails"]\n [data-supertokens~="passkeyFeatureBlockSubText"] {\n font-size: var(--font-size-0);\n font-weight: 400;\n line-height: 13.8px;\n text-align: left;\n text-underline-position: from-font;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n color: rgb(var(--palette-textGray));\n margin-top: 9px;\n}\n[data-supertokens~="passkeyConfirmationContainer"] [data-supertokens~="passkeyConfirmationFooter"] {\n margin-top: 25px;\n}\n[data-supertokens~="somethingWentWrongContainer"]\n [data-supertokens~="somethingWentWrongErrorDetailsContainer"]\n [data-supertokens~="label"] {\n margin-top: 4px;\n font-size: var(--font-size-4);\n font-weight: 500;\n line-height: 40px;\n letter-spacing: 0.58px;\n text-align: center;\n text-underline-position: from-font;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n color: #222222;\n}\n[data-supertokens~="somethingWentWrongContainer"]\n [data-supertokens~="somethingWentWrongErrorDetailsContainer"]\n [data-supertokens~="errorDetails"] {\n font-size: var(--font-size-1);\n font-weight: 400;\n line-height: 21px;\n letter-spacing: 0.4px;\n text-align: center;\n text-underline-position: from-font;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n color: #656565;\n}\n[data-supertokens~="somethingWentWrongContainer"] [data-supertokens~="goBackButtonContainer"] {\n margin-top: 22px;\n}\n[data-supertokens~="somethingWentWrongContainer"]\n [data-supertokens~="goBackButtonContainer"]\n [data-supertokens~="errorGoBackLabel"] {\n font-size: var(--font-size-1);\n font-weight: 500;\n line-height: 21px;\n text-align: center;\n text-underline-position: from-font;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n color: #0076ff;\n}\n[data-supertokens~="passkeySignInContainer"] {\n margin-bottom: 6px;\n}\n[data-supertokens~="passkeyRecoverAccountFormContainer"]\n [data-supertokens~="passkeyRecoverAccountFormHeaderWrapper"]\n [data-supertokens~="passkeyRecoverAccountFormHeader"] {\n padding-top: 10px;\n}\n[data-supertokens~="passkeyRecoverAccountFormContainer"]\n [data-supertokens~="passkeyRecoverAccountFormHeaderWrapper"]\n [data-supertokens~="passkeyRecoverAccountFormSubHeader"] {\n width: 75%;\n margin: 0 auto;\n font-size: var(--font-size-1);\n font-weight: 400;\n line-height: 16.1px;\n text-align: center;\n text-underline-position: from-font;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n color: rgb(var(--palette-textGray));\n margin-bottom: 20px;\n}\n[data-supertokens~="passkeyRecoverAccountFormContainer"] [data-supertokens~="errorContainer"] {\n margin-bottom: 15px;\n}\n[data-supertokens~="passkeyEmailSentContainer"] [data-supertokens~="emailSentDescription"] {\n margin: 0 auto;\n font-size: var(--font-size-1);\n font-weight: 400;\n line-height: 16.1px;\n text-align: center;\n text-underline-position: from-font;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n color: rgb(var(--palette-textGray));\n}\n[data-supertokens~="passkeyEmailSentContainer"]\n [data-supertokens~="emailSentDescription"]\n [data-supertokens~="changeEmailBtn"] {\n line-height: 16.1px;\n}\n[data-supertokens~="passkeyRecoverAccountSuccessContainer"]\n [data-supertokens~="header"]\n [data-supertokens~="headerText"] {\n margin-top: 20px;\n font-size: 20px;\n font-weight: 700;\n line-height: 30px;\n text-align: center;\n text-underline-position: from-font;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n color: rgb(var(--palette-textTitle));\n}\n[data-supertokens~="passkeyMfaSignInDivider"] {\n color: rgb(var(--palette-textPrimary));\n font-size: var(--font-size-2);\n display: flex;\n align-items: center;\n gap: 15px;\n}\n'; + +var ThemeBase = function (_a) { + var children = _a.children, + userStyles = _a.userStyles; + return jsxRuntime.jsxs(React.Fragment, { + children: [children, jsxRuntime.jsxs("style", { children: [styles, userStyles.join("\n")] })], + }); +}; + +var WebauthnMFALoadingScreen = uiEntry.withOverride("WebauthnMFALoadingScreen", function WebauthnMFALoadingScreen() { + return jsxRuntime.jsx( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "container delayedRender pwless-mfa loadingScreen" }, + { + children: jsxRuntime.jsx( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "row" }, + { + children: jsxRuntime.jsx( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "spinner delayedRender" }, + { children: jsxRuntime.jsx(uiEntry.SpinnerIcon, {}) } + ) + ), + } + ) + ), + } + ) + ); +}); var PasskeyNotSupportedError = uiEntry.withOverride("WebauthnPasskeyNotSupportedError", function () { var t = translationContext.useTranslation(); @@ -111,30 +125,258 @@ var PasskeyNotSupportedError = uiEntry.withOverride("WebauthnPasskeyNotSupported ); }); -var ContinueWithoutPasskey = uiEntry.withOverride( - "WebauthnContinueWithoutPasskey", - function ContinueWithoutPasskeyButton(props) { - var t = translationContext.useTranslation(); - return jsxRuntime.jsx( - "div", - genericComponentOverrideContext.__assign( - { "data-supertokens": "continueWithoutPasskey" }, - { - children: jsxRuntime.jsx( - "a", - genericComponentOverrideContext.__assign( - { - onClick: props.onClick, - "data-supertokens": "formLabelLinkBtn continueWithoutPasskeyLabel", - }, - { children: t("WEBAUTHN_CONTINUE_WITHOUT_PASSKEY_BUTTON") } - ) - ), - } - ) - ); - } -); +var WebauthnMFASignIn = uiEntry.withOverride("WebauthnMFASignIn", function WebauthnMFASignIn(props) { + var _this = this; + var t = translationContext.useTranslation(); + var _a = React__namespace.useState(false), + isLoading = _a[0], + setIsLoading = _a[1]; + var onClick = React__namespace.useCallback( + function () { + return genericComponentOverrideContext.__awaiter(_this, void 0, void 0, function () { + return genericComponentOverrideContext.__generator(this, function (_a) { + switch (_a.label) { + case 0: + setIsLoading(true); + return [4 /*yield*/, props.onSignIn()]; + case 1: + _a.sent(); + setIsLoading(false); + return [2 /*return*/]; + } + }); + }); + }, + [props] + ); + return jsxRuntime.jsxs(React.Fragment, { + children: [ + props.onBackButtonClicked + ? jsxRuntime.jsxs( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "headerTitle withBackButton webauthn-mfa" }, + { + children: [ + jsxRuntime.jsx(uiEntry.BackButton, { onClick: props.onBackButtonClicked }), + t("WEBAUTHN_MFA_SIGN_IN_HEADER_TITLE"), + jsxRuntime.jsx("span", { + "data-supertokens": "backButtonPlaceholder backButtonCommon", + }), + ], + } + ) + ) + : jsxRuntime.jsx( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "headerTitle webauthn-mfa" }, + { children: t("WEBAUTHN_MFA_SIGN_IN_HEADER_TITLE") } + ) + ), + jsxRuntime.jsx( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "headerSubtitle secondaryText" }, + { children: t("WEBAUTHN_MFA_SIGN_IN_HEADER_SUBTITLE") } + ) + ), + jsxRuntime.jsx(button.Button, { + disabled: !props.deviceSupported || isLoading, + isLoading: isLoading, + type: "button", + onClick: onClick, + label: "WEBAUTHN_EMAIL_CONTINUE_BUTTON", + isGreyedOut: !props.deviceSupported, + icon: componentOverrideContext.PasskeyIcon, + }), + props.error !== undefined && jsxRuntime.jsx(uiEntry.GeneralError, { error: props.error }), + props.canRegisterPasskey && + jsxRuntime.jsxs(jsxRuntime.Fragment, { + children: [ + jsxRuntime.jsxs( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "passkeyMfaSignInDivider" }, + { + children: [ + jsxRuntime.jsx("div", { "data-supertokens": "divider" }), + jsxRuntime.jsx("span", { children: t("WEBAUTHN_MFA_DIVIDER") }), + jsxRuntime.jsx("div", { "data-supertokens": "divider" }), + ], + } + ) + ), + jsxRuntime.jsxs( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "headerSubtitle secondaryText" }, + { + children: [ + jsxRuntime.jsx( + "span", + genericComponentOverrideContext.__assign( + { "data-supertokens": "link", onClick: props.onRegisterPasskeyClick }, + { children: t("WEBAUTHN_MFA_REGISTER_PASSKEY_TITLE") } + ) + ), + t("WEBAUTHN_MFA_REGISTER_PASSKEY_SUBTITLE"), + ], + } + ) + ), + ], + }), + !props.deviceSupported && jsxRuntime.jsx(PasskeyNotSupportedError, {}), + ], + }); +}); + +var WebauthnMFASignUp = uiEntry.withOverride("WebauthnMFASignUp", function WebauthnMFASignUp(props) { + var _this = this; + var t = translationContext.useTranslation(); + var onSuccess = React__namespace.useCallback( + function (_a) { + var email = _a.email; + props.onContinueClick(email); + }, + [props.onContinueClick] + ); + return jsxRuntime.jsxs(React.Fragment, { + children: [ + props.onBackButtonClicked + ? jsxRuntime.jsxs( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "headerTitle withBackButton webauthn-mfa" }, + { + children: [ + jsxRuntime.jsx(uiEntry.BackButton, { onClick: props.onBackButtonClicked }), + t("WEBAUTHN_MFA_REGISTER_PASSKEY_TITLE"), + jsxRuntime.jsx("span", { + "data-supertokens": "backButtonPlaceholder backButtonCommon", + }), + ], + } + ) + ) + : jsxRuntime.jsx( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "headerTitle webauthn-mfa" }, + { children: t("WEBAUTHN_MFA_REGISTER_PASSKEY_TITLE") } + ) + ), + props.error !== undefined && jsxRuntime.jsx(uiEntry.GeneralError, { error: props.error }), + jsxRuntime.jsxs( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "signUpFormInnerContainer" }, + { + children: [ + jsxRuntime.jsx( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "cautionMessage" }, + { children: t("WEBAUTHN_SIGN_UP_CAUTION_MESSAGE_LABEL") } + ) + ), + jsxRuntime.jsx(formBase.FormBase, { + clearError: props.clearError, + onFetchError: props.onFetchError, + onError: props.onError, + formFields: [ + { + id: "email", + label: "", + labelComponent: jsxRuntime.jsxs( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "formLabelWithLinkWrapper" }, + { + children: [ + jsxRuntime.jsx(formBase.Label, { + value: "WEBAUTHN_SIGN_UP_LABEL", + "data-supertokens": "emailInputLabel", + }), + jsxRuntime.jsx( + "a", + genericComponentOverrideContext.__assign( + { + onClick: props.onRecoverAccountClick, + "data-supertokens": + "link linkButton formLabelLinkBtn recoverAccountTrigger", + }, + { children: t("WEBAUTHN_RECOVER_ACCOUNT_LABEL") } + ) + ), + ], + } + ) + ), + optional: false, + autofocus: true, + placeholder: "", + getDefaultValue: function () { + return props.email; + }, + autoComplete: "email", + // We are using the default validator that allows any string + validate: validators.defaultEmailValidator, + }, + ], + buttonLabel: "WEBAUTHN_EMAIL_CONTINUE_BUTTON", + onSuccess: onSuccess, + callAPI: function (formFields) { + return genericComponentOverrideContext.__awaiter( + _this, + void 0, + void 0, + function () { + var email; + var _a; + return genericComponentOverrideContext.__generator(this, function (_b) { + email = + (_a = formFields.find(function (field) { + return field.id === "email"; + })) === null || _a === void 0 + ? void 0 + : _a.value; + if (email === undefined) { + throw new STGeneralError__default.default( + "GENERAL_ERROR_EMAIL_UNDEFINED" + ); + } + if (email === "") { + throw new STGeneralError__default.default( + "EMAIL_INPUT_NOT_POPULATED_ERROR" + ); + } + // We do not want the form to make the API call since we have + // an intermediary step here so we will just mock an OK status + // to render the next step. + return [ + 2 /*return*/, + { + status: "OK", + email: email, + }, + ]; + }); + } + ); + }, + validateOnBlur: false, + showLabels: true, + footer: undefined, + }), + ], + } + ) + ), + ], + }); +}); /* Copyright (c) 2025, VRAI Labs and/or its affiliates. All rights reserved. * @@ -198,7 +440,984 @@ function MultipleDevicesIcon() { ); } -/* Copyright (c) 2025, VRAI Labs and/or its affiliates. All rights reserved. +/* Copyright (c) 2025, 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. + */ +function SecurityIcon() { + return jsxRuntime.jsx( + "svg", + genericComponentOverrideContext.__assign( + { width: "16", height: "20", viewBox: "0 0 16 20", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, + { + children: jsxRuntime.jsx("path", { + "fill-rule": "evenodd", + "clip-rule": "evenodd", + d: "M7.74971 0.0115695C7.7268 0.0212227 7.6556 0.0538204 7.59151 0.0839829C7.52743 0.114175 7.46681 0.13888 7.45681 0.13888C7.44681 0.13888 7.39926 0.157687 7.35112 0.180661C7.30298 0.203635 7.16983 0.263813 7.05525 0.314426C6.94066 0.36501 6.8219 0.418234 6.79135 0.432699C6.76079 0.447164 6.68579 0.480759 6.62467 0.507342C6.37589 0.615551 5.77415 0.875893 5.51353 0.988063C5.36074 1.05385 5.19824 1.12541 5.1524 1.14715C4.95626 1.24013 4.84114 1.29074 4.60127 1.3895C4.46132 1.44713 4.33432 1.50388 4.31904 1.51555C4.30376 1.52726 4.18501 1.58004 4.05514 1.63283C3.92528 1.68558 3.80713 1.74074 3.79263 1.75539C3.7781 1.77 3.75035 1.78197 3.73096 1.78197C3.71155 1.78197 3.62965 1.8133 3.54901 1.85156C3.46834 1.88985 3.31484 1.95737 3.20789 2.00158C3.10094 2.0458 3.00094 2.0921 2.98566 2.10442C2.97038 2.11678 2.92663 2.13667 2.88843 2.14864C2.85024 2.16061 2.76899 2.1935 2.70787 2.22179C2.64676 2.25004 2.51528 2.30708 2.41569 2.34851C2.31611 2.38997 2.19735 2.44443 2.1518 2.46951C2.10624 2.4946 2.05891 2.5152 2.0466 2.51531C2.01979 2.51552 1.79481 2.61622 1.68475 2.67725C1.6415 2.70125 1.595 2.72088 1.58142 2.72088C1.5678 2.72088 1.48839 2.75388 1.40488 2.79423C1.32138 2.83457 1.24477 2.86758 1.23463 2.86758C1.21565 2.86758 1.15515 2.89487 0.883142 3.02608C0.796222 3.06801 0.716997 3.10231 0.70708 3.10231C0.697135 3.10231 0.610744 3.14057 0.515101 3.18734C0.287482 3.2986 0.144588 3.43447 0.0647523 3.61557L0 3.76248L0.000472403 6.16843C0.000722412 7.4917 0.0127228 8.69321 0.0271122 8.83845C0.0954757 9.52819 0.136449 9.87641 0.163506 9.99741C0.179729 10.07 0.210841 10.2351 0.232619 10.3642C0.282788 10.6612 0.357707 11.0257 0.380402 11.083C0.390014 11.1072 0.419598 11.2129 0.44621 11.3177C0.547602 11.7175 0.588604 11.8615 0.609132 11.8899C0.620799 11.906 0.645578 11.9851 0.66419 12.0656C0.682802 12.146 0.707886 12.2252 0.719914 12.2416C0.73197 12.2579 0.750693 12.3105 0.761554 12.3583C0.782722 12.4515 0.86303 12.6589 0.930394 12.7945C0.953117 12.8402 0.9717 12.8917 0.9717 12.9091C0.9717 12.9899 1.65945 14.4387 1.72056 14.4866C1.73087 14.4946 1.78167 14.5805 1.83348 14.6773C1.88529 14.7741 1.93793 14.8665 1.95043 14.8827C1.96296 14.8988 2.01304 14.978 2.06171 15.0587C2.11041 15.1394 2.15699 15.212 2.16524 15.2201C2.17349 15.2282 2.21769 15.294 2.2635 15.3664C2.35014 15.5034 2.68262 15.9729 2.73396 16.0309C2.75018 16.0491 2.80718 16.1216 2.86066 16.1918C3.36731 16.8573 4.44152 17.931 4.99998 18.3302C5.03384 18.3544 5.11323 18.4147 5.17643 18.4643C5.34457 18.5961 5.63792 18.8076 5.74964 18.8775C5.80312 18.911 5.9301 18.9924 6.03185 19.0585C6.23508 19.1903 6.59345 19.3957 6.91635 19.5653C7.09641 19.6599 7.19069 19.706 7.51359 19.8574C7.97342 20.073 8.08467 20.06 8.79142 19.7087C9.03984 19.5852 9.3118 19.4446 9.40788 19.3899C9.72578 19.2089 9.98768 19.0479 9.99515 19.0287C10.0003 19.0157 10.0167 19.0051 10.0316 19.0051C10.0698 19.0051 10.6872 18.5723 11.0249 18.3089C12.0679 17.4951 13.1408 16.3045 13.8714 15.1499C14.1104 14.7723 14.3209 14.4174 14.3702 14.3091C14.3808 14.2857 14.4219 14.2071 14.4615 14.1345C14.5716 13.9323 14.8431 13.3536 14.9432 13.1075C14.9925 12.9865 15.0499 12.8479 15.0709 12.7995C15.1328 12.6565 15.3046 12.1685 15.3628 11.9706C15.3924 11.8697 15.4429 11.6981 15.4749 11.5892C15.6687 10.9305 15.8341 10.0872 15.9023 9.41059C15.9243 9.19274 15.9553 8.90077 15.9711 8.76178C15.9889 8.60642 16 7.599 16 6.14675V3.78445L15.9332 3.62493C15.8529 3.4331 15.7179 3.30153 15.4843 3.18734C15.3887 3.14057 15.3023 3.10231 15.2924 3.10231C15.2824 3.10231 15.2032 3.06801 15.1163 3.02608C14.8443 2.89487 14.7838 2.86758 14.7648 2.86758C14.7547 2.86758 14.6781 2.83457 14.5946 2.79423C14.5111 2.75388 14.4316 2.72088 14.418 2.72088C14.4044 2.72088 14.3579 2.70125 14.3147 2.67725C14.2046 2.61622 13.9797 2.51552 13.9528 2.51531C13.9405 2.5152 13.8932 2.4946 13.8476 2.46951C13.8021 2.44443 13.6833 2.38997 13.5837 2.34851C13.4842 2.30708 13.3527 2.25004 13.2916 2.22179C13.2305 2.1935 13.1492 2.16061 13.111 2.14864C13.0728 2.13667 13.0291 2.11678 13.0138 2.10442C12.9985 2.0921 12.8985 2.04577 12.7916 2.00153C12.6846 1.95728 12.5471 1.89754 12.486 1.86882C12.4249 1.84006 12.2311 1.75533 12.0554 1.68048C11.8797 1.60563 11.6985 1.52623 11.6526 1.50402C11.5737 1.46573 11.1657 1.28666 10.9859 1.21141C10.9401 1.19219 10.8714 1.16126 10.8332 1.14263C10.795 1.12403 10.6387 1.05473 10.4859 0.98865C10.1118 0.826835 9.51158 0.566845 9.37477 0.507342C9.31366 0.480759 9.23865 0.447164 9.2081 0.432699C9.17754 0.418234 9.05879 0.36501 8.9442 0.314426C8.82961 0.263813 8.69647 0.203635 8.64833 0.180661C8.60019 0.157687 8.55263 0.13888 8.54263 0.13888C8.53263 0.13888 8.46577 0.109685 8.39404 0.0739775C8.29754 0.0259465 8.20223 0.00710975 8.0275 0.00153498C7.89764 -0.00260208 7.77263 0.00191639 7.74971 0.0115695ZM8.04872 1.66343C8.07525 1.67895 8.1907 1.73276 8.30529 1.78305C8.41988 1.83331 8.55302 1.89326 8.60116 1.91623C8.6493 1.93921 8.69741 1.95801 8.70808 1.95801C8.71875 1.95801 8.76686 1.97682 8.815 1.99979C8.86314 2.02277 8.99629 2.08295 9.11087 2.13356C9.22546 2.18414 9.34421 2.23737 9.37477 2.25183C9.44263 2.28396 9.57897 2.34338 9.88868 2.47573C10.0185 2.53125 10.1642 2.59591 10.2123 2.61942C10.2605 2.64295 10.3081 2.66219 10.3182 2.66219C10.3283 2.66219 10.4077 2.69649 10.4946 2.73842C10.7666 2.86963 10.8271 2.89692 10.8461 2.89692C10.8562 2.89692 10.9328 2.92993 11.0163 2.97027C11.0998 3.01062 11.1792 3.04363 11.1929 3.04363C11.2064 3.04363 11.2529 3.06325 11.2962 3.08726C11.4063 3.14828 11.6312 3.24898 11.658 3.24919C11.6704 3.2493 11.7177 3.2699 11.7632 3.29499C11.8088 3.32008 11.9276 3.37453 12.0271 3.41599C12.1267 3.45742 12.2582 3.51446 12.3193 3.54271C12.3804 3.571 12.4617 3.60389 12.4999 3.61586C12.5381 3.62783 12.5818 3.64772 12.5971 3.66008C12.6124 3.6724 12.7124 3.7187 12.8193 3.76292C12.9263 3.80713 13.0798 3.87465 13.1605 3.91294C13.2411 3.9512 13.323 3.98253 13.3424 3.98253C13.3618 3.98253 13.3896 3.9945 13.4041 4.00912C13.4186 4.02376 13.5367 4.07892 13.6666 4.13167C13.7965 4.18446 13.9152 4.23712 13.9305 4.24871C13.9458 4.2603 14.0645 4.31312 14.1944 4.36608C14.3242 4.41904 14.4461 4.47487 14.4652 4.49013C14.5184 4.53262 14.5127 7.38484 14.4575 8.33965C14.4131 9.10897 14.3956 9.27967 14.3074 9.80669C14.2208 10.3237 14.167 10.5956 14.1417 10.6439C14.1287 10.6686 14.1095 10.7346 14.0989 10.7906C14.0777 10.9029 13.9523 11.3573 13.917 11.4498C13.9046 11.4821 13.8603 11.6141 13.8185 11.7432C13.7767 11.8723 13.7263 12.0175 13.7066 12.0659C13.6869 12.1144 13.6313 12.253 13.5829 12.374C13.3094 13.0591 12.8567 13.8902 12.3467 14.6433C11.9461 15.235 11.19 16.0899 10.6375 16.576C10.4436 16.7465 9.96746 17.1328 9.8314 17.2299C9.76359 17.2783 9.69348 17.3312 9.67556 17.3473C9.65767 17.3634 9.5595 17.4295 9.45741 17.494C9.35533 17.5586 9.23002 17.6402 9.1789 17.6755C9.12779 17.7107 8.96345 17.8086 8.8137 17.8929C8.66394 17.9772 8.52266 18.0569 8.49974 18.07C8.47682 18.0831 8.35365 18.147 8.22604 18.2119C8.00886 18.3224 7.98933 18.3273 7.92047 18.2874C7.88002 18.264 7.72071 18.1795 7.56643 18.0997C7.25634 17.9394 6.95352 17.7669 6.81679 17.6728C6.76774 17.639 6.64412 17.5586 6.54203 17.494C6.43995 17.4295 6.34178 17.3634 6.32389 17.3473C6.30597 17.3312 6.23561 17.2783 6.16752 17.2299C6.03107 17.1329 5.55622 16.7476 5.36196 16.5763C4.89767 16.1667 4.34221 15.5639 3.94203 15.0353C3.80519 14.8545 3.68132 14.6911 3.66674 14.6722C3.42681 14.3603 2.83727 13.3248 2.60053 12.7995C2.52781 12.6381 2.45817 12.4858 2.44575 12.461C2.34239 12.2547 2.01299 11.2798 1.91079 10.8776C1.57408 9.55287 1.4995 8.69893 1.4995 6.16946V4.49227L1.70089 4.40944C1.81167 4.36388 1.96479 4.2971 2.04118 4.26101C2.11757 4.22492 2.26758 4.15937 2.37453 4.11533C2.48148 4.07129 2.58084 4.02338 2.59537 4.00888C2.60987 3.99439 2.63765 3.98253 2.65704 3.98253C2.67645 3.98253 2.75835 3.9512 2.83899 3.91294C2.91966 3.87465 3.07316 3.80713 3.18011 3.76292C3.28706 3.7187 3.38706 3.6724 3.40234 3.66008C3.41762 3.64772 3.46137 3.62795 3.49957 3.6161C3.53776 3.60427 3.60651 3.57707 3.65235 3.55565C3.69818 3.53426 3.81694 3.48154 3.91625 3.43852C4.01556 3.39551 4.14926 3.33524 4.21334 3.30464C4.27743 3.27404 4.33751 3.24901 4.34682 3.24901C4.36904 3.24901 4.60313 3.14277 4.70325 3.08726C4.7465 3.06325 4.793 3.04363 4.80658 3.04363C4.8202 3.04363 4.89962 3.01062 4.98312 2.97027C5.06662 2.92993 5.14323 2.89692 5.15337 2.89692C5.17235 2.89692 5.23285 2.86963 5.50486 2.73842C5.59178 2.69649 5.67303 2.66175 5.68542 2.6612C5.69784 2.66064 5.75798 2.6347 5.81909 2.60351C5.8802 2.57232 5.94082 2.54639 5.95379 2.54583C5.96676 2.54527 6.01676 2.52602 6.06491 2.50305C6.11305 2.48008 6.24619 2.4199 6.36078 2.36928C6.47536 2.3187 6.59412 2.26565 6.62467 2.25142C6.65523 2.23719 6.77399 2.18414 6.88857 2.13356C7.00316 2.08295 7.1363 2.02277 7.18444 1.99979C7.23258 1.97682 7.2807 1.95801 7.29136 1.95801C7.30203 1.95801 7.35014 1.93921 7.39828 1.91623C7.44643 1.89326 7.57957 1.83334 7.69416 1.78308C7.80874 1.73282 7.92125 1.67977 7.94417 1.66519C7.99828 1.6308 7.993 1.63089 8.04872 1.66343ZM10.5415 6.91627C10.4706 6.95341 9.84179 7.59639 8.80606 8.69069L7.18172 10.4068L6.34764 9.53438C5.60475 8.75738 5.49791 8.65636 5.37091 8.61082C5.17354 8.54008 5.00245 8.54615 4.82236 8.63027C4.63708 8.71683 4.52999 8.82272 4.43407 9.01426C4.33568 9.21069 4.3311 9.50152 4.42321 9.70066C4.51132 9.89117 6.68334 12.1849 6.86441 12.2787C7.04097 12.3701 7.31914 12.3701 7.49665 12.2788C7.59138 12.23 8.12856 11.6818 9.55947 10.1736C11.6846 7.93357 11.6387 7.98938 11.6387 7.64548C11.6387 7.33672 11.44 7.01465 11.1782 6.8991C11.0344 6.83564 10.6768 6.84526 10.5415 6.91627Z", + fill: "#1C222A", + }), + } + ) + ); +} + +var blockDetails = [ + { + title: "WEBAUTHN_FEATURE_BLOCK_NO_NEED_TO_REMEMBER_PASSWORD", + subText: "WEBAUTHN_FEATURE_BLOCK_NO_NEED_TO_REMEMBER_PASSWORD_DETAIL", + icon: jsxRuntime.jsx(FingerPrintIcon, {}), + }, + { + title: "WEBAUTHN_FEATURE_BLOCK_WORKS_ON_ALL_DEVICES", + subText: "WEBAUTHN_FEATURE_BLOCK_WORKS_ON_ALL_DEVICES_DETAIL", + icon: jsxRuntime.jsx(MultipleDevicesIcon, {}), + }, + { + title: "WEBAUTHN_FEATURE_BLOCK_KEEP_ACCOUNT_SAFER", + subText: "WEBAUTHN_FEATURE_BLOCK_KEEP_ACCOUNT_SAFER_DETAIL", + icon: jsxRuntime.jsx(SecurityIcon, {}), + }, +]; +var PasskeyFeatureBlock = uiEntry.withOverride("WebauthnPasskeyFeatureBlock", function FeatureBlock(props) { + var t = translationContext.useTranslation(); + return jsxRuntime.jsxs( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "passkeyFeatureBlock" }, + { + children: [ + jsxRuntime.jsx( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "passkeyFeatureBlockIcon" }, + { children: props.icon } + ) + ), + jsxRuntime.jsxs( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "passkeyFeatureBlockDetails" }, + { + children: [ + jsxRuntime.jsx( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "passkeyFeatureBlockTitle" }, + { children: t(props.title) } + ) + ), + jsxRuntime.jsx( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "passkeyFeatureBlockSubText" }, + { children: t(props.subText) } + ) + ), + ], + } + ) + ), + ], + } + ) + ); +}); +var PasskeyFeatureBlockList = function () { + return jsxRuntime.jsx( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "passkeyFeatureBlocksContainer" }, + { + children: blockDetails.map(function (blockDetail, index) { + return jsxRuntime.jsx( + PasskeyFeatureBlock, + genericComponentOverrideContext.__assign({}, blockDetail), + "".concat(blockDetail.title, "-").concat(index) + ); + }), + } + ) + ); +}; + +var WebauthnMFASignUpConfirmation = uiEntry.withOverride( + "WebauthnMFASignUpConfirmation", + function WebauthnMFASignUpConfirmation(props) { + var _this = this; + var t = translationContext.useTranslation(); + var _a = React__namespace.useState(false), + isLoading = _a[0], + setIsLoading = _a[1]; + var onClick = React__namespace.useCallback( + function () { + return genericComponentOverrideContext.__awaiter(_this, void 0, void 0, function () { + return genericComponentOverrideContext.__generator(this, function (_a) { + switch (_a.label) { + case 0: + setIsLoading(true); + return [4 /*yield*/, props.onSignUp()]; + case 1: + _a.sent(); + setIsLoading(false); + return [2 /*return*/]; + } + }); + }); + }, + [props.onSignUp] + ); + return jsxRuntime.jsxs(React.Fragment, { + children: [ + props.onBackButtonClicked + ? jsxRuntime.jsxs( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "headerTitle withBackButton webauthn-mfa" }, + { + children: [ + jsxRuntime.jsx(uiEntry.BackButton, { onClick: props.onBackButtonClicked }), + t("WEBAUTHN_MFA_REGISTER_PASSKEY_TITLE"), + jsxRuntime.jsx("span", { + "data-supertokens": "backButtonPlaceholder backButtonCommon", + }), + ], + } + ) + ) + : jsxRuntime.jsx( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "headerTitle webauthn-mfa" }, + { children: t("WEBAUTHN_MFA_REGISTER_PASSKEY_TITLE") } + ) + ), + jsxRuntime.jsx("div", { "data-supertokens": "divider" }), + jsxRuntime.jsxs( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "passkeyConfirmationContainer" }, + { + children: [ + jsxRuntime.jsxs( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "passkeyConfirmationEmailContainer" }, + { + children: [ + jsxRuntime.jsx( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "continueWithLabel" }, + { children: t("WEBAUTHN_CONTINUE_WITH_EMAIL_SUBTEXT") } + ) + ), + jsxRuntime.jsx( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "enteredEmailId" }, + { children: props.email } + ) + ), + ], + } + ) + ), + jsxRuntime.jsx(PasskeyFeatureBlockList, {}), + props.error !== undefined && + jsxRuntime.jsx(uiEntry.GeneralError, { error: props.error }), + jsxRuntime.jsx( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "passkeyConfirmationFooter" }, + { + children: jsxRuntime.jsx(button.Button, { + disabled: isLoading, + isLoading: isLoading, + type: "button", + onClick: onClick, + label: "WEBAUTHN_EMAIL_CONTINUE_BUTTON", + isGreyedOut: false, + }), + } + ) + ), + ], + } + ) + ), + ], + }); + } +); + +var MFAScreens; +(function (MFAScreens) { + MFAScreens[(MFAScreens["SignIn"] = 0)] = "SignIn"; + MFAScreens[(MFAScreens["SignUp"] = 1)] = "SignUp"; + MFAScreens[(MFAScreens["SignUpConfirmation"] = 2)] = "SignUpConfirmation"; +})(MFAScreens || (MFAScreens = {})); +function MFAThemeWrapper(props) { + var rootStyle = genericComponentOverrideContext.SuperTokens.getInstanceOrThrow().rootStyle; + return jsxRuntime.jsx( + uiEntry.UserContextWrapper, + genericComponentOverrideContext.__assign( + { userContext: props.userContext }, + { + children: jsxRuntime.jsx( + ThemeBase, + genericComponentOverrideContext.__assign( + { userStyles: [rootStyle, props.config.recipeRootStyle] }, + { children: jsxRuntime.jsx(MFATheme, genericComponentOverrideContext.__assign({}, props)) } + ) + ), + } + ) + ); +} +function MFATheme(props) { + var t = translationContext.useTranslation(); + if (!props.featureState.loaded) { + return jsxRuntime.jsx(WebauthnMFALoadingScreen, {}); + } + if (props.featureState.accessDenied) { + return jsxRuntime.jsx(sessionprebuiltui.AccessDeniedScreen, { + useShadowDom: false /* We set this to false, because we are already inside a shadowDom (if required) */, + error: t(props.featureState.error), + }); + } + return jsxRuntime.jsxs( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "container webauthn-mfa" }, + { + children: [ + jsxRuntime.jsx( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "row" }, + { + children: jsxRuntime.jsx( + MFAThemeRouter, + genericComponentOverrideContext.__assign({}, props) + ), + } + ) + ), + jsxRuntime.jsx(uiEntry.SuperTokensBranding, {}), + ], + } + ) + ); +} +function MFAThemeRouter(props) { + var _this = this; + var onBackButtonClicked = props.onBackButtonClicked, + onSignIn = props.onSignIn; + var _a = React__namespace.useState(function () { + if (!props.featureState.hasRegisteredPassKey) { + return props.featureState.email ? MFAScreens.SignUpConfirmation : MFAScreens.SignUp; + } + return MFAScreens.SignIn; + }), + activeScreen = _a[0], + setActiveScreen = _a[1]; + var _b = React__namespace.useState(""), + email = _b[0], + setEmail = _b[1]; + var signUpEmail = props.featureState.email || email; + var onSignUpContinue = React__namespace.useCallback( + function (email) { + if (!props.featureState.canRegisterPasskey) { + return; + } + setActiveScreen(MFAScreens.SignUpConfirmation); + setEmail(email); + }, + [props.featureState.canRegisterPasskey] + ); + var onRegisterPasskeyClick = React__namespace.useCallback( + function () { + if (!props.featureState.canRegisterPasskey) { + return; + } + if (props.featureState.email) { + setActiveScreen(MFAScreens.SignUpConfirmation); + } else { + setActiveScreen(MFAScreens.SignUp); + } + }, + [props.featureState.email, props.featureState.canRegisterPasskey] + ); + var clearError = React__namespace.useCallback( + function () { + props.dispatch({ type: "setError", error: undefined }); + }, + [props] + ); + var onError = React__namespace.useCallback( + function (error) { + props.dispatch({ type: "setError", error: error }); + }, + [props] + ); + var onClickSignUpConfirmationBackButton = React__namespace.useCallback( + function () { + if (!props.featureState.email) { + setActiveScreen(MFAScreens.SignUp); + return; + } + if (!props.featureState.hasRegisteredPassKey && !props.featureState.showBackButton) { + return; + } + if (!props.featureState.hasRegisteredPassKey) { + onBackButtonClicked(); + return; + } + setActiveScreen(MFAScreens.SignIn); + }, + [ + props.featureState.email, + props.featureState.hasRegisteredPassKey, + props.featureState.showBackButton, + onBackButtonClicked, + ] + ); + var showBackButtonOnSignUpConfirmation = React__namespace.useMemo( + function () { + return ( + props.featureState.email !== undefined || + props.featureState.hasRegisteredPassKey || + props.featureState.showBackButton + ); + }, + [props.featureState.email, props.featureState.hasRegisteredPassKey, props.featureState.showBackButton] + ); + var onSignUp = React__namespace.useCallback( + function () { + return genericComponentOverrideContext.__awaiter(_this, void 0, void 0, function () { + return genericComponentOverrideContext.__generator(this, function (_a) { + switch (_a.label) { + case 0: + return [4 /*yield*/, props.onSignUp(signUpEmail)]; + case 1: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }, + [props.onSignUp, signUpEmail] + ); + var onFetchError = React__namespace.useCallback( + function () { + onError("SOMETHING_WENT_WRONG_ERROR"); + }, + [onError] + ); + var onClickSignUpBackButton = React__namespace.useCallback( + function () { + if (!props.featureState.hasRegisteredPassKey && !props.featureState.showBackButton) { + return; + } + if (!props.featureState.hasRegisteredPassKey) { + onBackButtonClicked(); + return; + } + setActiveScreen(MFAScreens.SignIn); + }, + [props.featureState.hasRegisteredPassKey, props.featureState.showBackButton, onBackButtonClicked] + ); + var showBackButtonOnSignUp = React__namespace.useMemo( + function () { + return props.featureState.hasRegisteredPassKey || props.featureState.showBackButton; + }, + [props.featureState.email, props.featureState.showBackButton] + ); + if (activeScreen === MFAScreens.SignUp) { + return jsxRuntime.jsx(WebauthnMFASignUp, { + clearError: clearError, + onError: onError, + onFetchError: onFetchError, + error: props.featureState.error, + onContinueClick: onSignUpContinue, + email: email, + onRecoverAccountClick: props.onRecoverAccountClick, + onBackButtonClicked: showBackButtonOnSignUp ? onClickSignUpBackButton : undefined, + }); + } + if (activeScreen === MFAScreens.SignUpConfirmation) { + return jsxRuntime.jsx(WebauthnMFASignUpConfirmation, { + onSignUp: onSignUp, + onBackButtonClicked: showBackButtonOnSignUpConfirmation ? onClickSignUpConfirmationBackButton : undefined, + email: signUpEmail, + error: props.featureState.error, + }); + } + return jsxRuntime.jsx(WebauthnMFASignIn, { + onBackButtonClicked: props.featureState.showBackButton ? onBackButtonClicked : undefined, + canRegisterPasskey: props.featureState.canRegisterPasskey, + onSignIn: onSignIn, + error: props.featureState.error, + deviceSupported: props.featureState.deviceSupported, + onRegisterPasskeyClick: onRegisterPasskeyClick, + }); +} + +var defaultTranslationsWebauthn = { + en: genericComponentOverrideContext.__assign( + genericComponentOverrideContext.__assign({}, genericComponentOverrideContext.defaultTranslationsCommon.en), + { + WEBAUTHN_EMAIL_CONTINUE_BUTTON: "CONTINUE", + WEBAUTHN_SIGN_UP_LABEL: "Email", + WEBAUTHN_RECOVER_ACCOUNT_LABEL: "Recover Account", + WEBAUTHN_RECOVER_ACCOUNT_SUBHEADER_LABEL: "We will send you an email to recover your account.", + WEBAUTHN_CONTINUE_WITHOUT_PASSKEY_BUTTON: "Continue without passkey", + WEBAUTHN_CREATE_A_PASSKEY_HEADER: "Create a passkey", + WEBAUTHN_CONTINUE_WITH_EMAIL_SUBTEXT: "Continue with", + WEBAUTHN_COMBO_CONTINUE_WITH_PASSKEY_BUTTON: "CONTINUE WITH PASSKEY", + WEBAUTHN_FEATURE_BLOCK_NO_NEED_TO_REMEMBER_PASSWORD: "No need to remember password", + WEBAUTHN_FEATURE_BLOCK_NO_NEED_TO_REMEMBER_PASSWORD_DETAIL: + "With passkey, you can use things like your face or fingerprint to login.", + WEBAUTHN_FEATURE_BLOCK_WORKS_ON_ALL_DEVICES: "Works on all devices", + WEBAUTHN_FEATURE_BLOCK_WORKS_ON_ALL_DEVICES_DETAIL: + "Passkey will automatically be available across all your synced devices.", + WEBAUTHN_FEATURE_BLOCK_KEEP_ACCOUNT_SAFER: "Keep your account safer", + WEBAUTHN_FEATURE_BLOCK_KEEP_ACCOUNT_SAFER_DETAIL: "Passkey offer state of the art phishing resistance.", + WEBAUTHN_PASSKEY_RECOVERABLE_ERROR: + "The request either timed out, was canceled or the device is already registered. Please try again or try using another device.", + WEBAUTHN_PASSKEY_INVALID_CREDENTIALS_ERROR: + "The passkey is invalid, please try again, possibly with a different device.", + WEBAUTHN_ERROR_GO_BACK_BUTTON_LABEL: "Go back", + WEBAUTHN_UNRECOVERABLE_ERROR: "Something went wrong", + WEBAUTHN_UNRECOVERABLE_ERROR_DETAILS: "Something went wrong with your current session. please try again.", + WEBAUTHN_EMAIL_SENT_LABEL: "Email sent", + WEBAUTHN_EMAIL_SENT_LABEL_PRE_EMAIL: "Account recovery email has been sent to ", + WEBAUTHN_EMAIL_SENT_LABEL_POST_EMAIL: ", if it exists in our system.", + WEBAUTHN_RESEND_OR_CHANGE_EMAIL_LABEL: "Resend or change email", + WEBAUTHN_ACCOUNT_RECOVERY_NOT_ALLOWED_LABEL: "Account Recovery is not allowed, please contact support.", + WEBAUTHN_ACCOUNT_RECOVERY_GENERAL_ERROR: + "Something went wrong while trying to send recover account token, please try again.", + WEBAUTHN_ACCOUNT_RECOVERY_SUCCESSFUL_LABEL: "Account recovered successfully!", + WEBAUTHN_ACCOUNT_RECOVERY_TOKEN_INVALID_ERROR: + "The token used for recovering the account is invalid. Please try with a different token or request a new one.", + WEBAUTHN_ACCOUNT_RECOVERY_INVALID_EMAIL_ERROR: + "The email used is invalid. Please try with a different email ID or reach out to support.", + WEBAUTHN_ACCOUNT_RECOVERY_INVALID_GENERATED_OPTIONS_ERROR: "Failed to recover account, please try again.", + WEBAUTHN_ACCOUNT_RECOVERY_FETCH_ERROR: + "Something went wrong, please refresh the page or reach out to support.", + WEBAUTHN_SIGN_UP_CAUTION_MESSAGE_LABEL: + "Make sure your email is correct—we’ll use it to help you recover your account.", + WEBAUTHN_ACCOUNT_RECOVERY_INVALID_CREDENTIALS_ERROR: + "The passkey is invalid, please try again, possibly with a different device.", + WEBAUTHN_ACCOUNT_RECOVERY_GENERATED_OPTIONS_NOT_FOUND_ERROR: "Failed to recover account, please try again.", + WEBAUTHN_ACCOUNT_RECOVERY_INVALID_AUTHENTICATOR_ERROR: "Invalid authenticator, please try again.", + WEBAUTHN_EMAIL_ALREADY_EXISTS_ERROR: "Email already exists, please sign in instead.", + WEBAUTHN_NOT_SUPPORTED_ERROR: + "Passkey is not supported on your browser, please try with a different browser.", + WEBAUTHN_PASSKEY_NOT_SUPPORTED_BY_BROWSER: + "Your browser does not support passkey flow, please try in a different browser.", + WEBAUTHN_EMAIL_INPUT_NOT_POPULATED_ERROR: "Please enter your email to continue.", + // WebAuthn MFA translations + WEBAUTHN_MFA_SIGN_IN_HEADER_TITLE: "Use a Passkey", + WEBAUTHN_MFA_SIGN_IN_HEADER_SUBTITLE: + "To finish signing in, click the button and follow the browser instructions.", + WEBAUTHN_MFA_DIVIDER: "or", + WEBAUTHN_MFA_REGISTER_PASSKEY_SUBTITLE: "Set up a new authentication method to use for future logins.", + WEBAUTHN_MFA_REGISTER_PASSKEY_TITLE: "Register a passkey", + } + ), +}; + +var useFeatureReducer = function () { + return React__namespace.useReducer( + function (oldState, action) { + switch (action.type) { + case "setError": + return genericComponentOverrideContext.__assign( + genericComponentOverrideContext.__assign({}, oldState), + { loaded: true, error: action.error, accessDenied: action.accessDenied || false } + ); + case "load": + return genericComponentOverrideContext.__assign( + genericComponentOverrideContext.__assign({}, oldState), + { + loaded: true, + deviceSupported: action.deviceSupported, + email: action.email, + showBackButton: action.showBackButton, + canRegisterPasskey: action.canRegisterPasskey, + hasRegisteredPassKey: action.hasRegisteredPassKey, + } + ); + default: + return oldState; + } + }, + { + error: undefined, + deviceSupported: false, + canRegisterPasskey: false, + hasRegisteredPassKey: false, + loaded: false, + showBackButton: true, + email: undefined, + accessDenied: false, + } + ); +}; +function useChildProps$2(recipe, recipeImplementation, state, dispatch, userContext, navigate) { + var _this = this; + var rethrowInRender = genericComponentOverrideContext.useRethrowInRender(); + var callSignInAPI = React__namespace.useCallback( + function (_, __) { + return genericComponentOverrideContext.__awaiter(_this, void 0, void 0, function () { + var response; + return genericComponentOverrideContext.__generator(this, function (_a) { + switch (_a.label) { + case 0: + return [ + 4 /*yield*/, + recipeImplementation.authenticateCredentialWithSignIn({ + shouldTryLinkingWithSessionUser: true, + userContext: userContext, + }), + ]; + case 1: + response = _a.sent(); + switch (response.status) { + case "INVALID_CREDENTIALS_ERROR": + dispatch({ type: "setError", error: "WEBAUTHN_PASSKEY_INVALID_CREDENTIALS_ERROR" }); + break; + case "FAILED_TO_AUTHENTICATE_USER": + case "INVALID_OPTIONS_ERROR": + dispatch({ type: "setError", error: "WEBAUTHN_PASSKEY_RECOVERABLE_ERROR" }); + break; + case "WEBAUTHN_NOT_SUPPORTED": + dispatch({ type: "setError", error: "WEBAUTHN_NOT_SUPPORTED_ERROR" }); + break; + } + return [2 /*return*/, response]; + } + }); + }); + }, + [recipeImplementation, userContext] + ); + var callSignUpAPI = React__namespace.useCallback( + function (email, _, __) { + return genericComponentOverrideContext.__awaiter(_this, void 0, void 0, function () { + var response; + return genericComponentOverrideContext.__generator(this, function (_a) { + switch (_a.label) { + case 0: + return [ + 4 /*yield*/, + recipeImplementation.registerCredentialWithSignUp({ + email: email, + shouldTryLinkingWithSessionUser: true, + userContext: userContext, + }), + ]; + case 1: + response = _a.sent(); + if (response.status !== "OK") { + dispatch({ type: "setError", error: "WEBAUTHN_PASSKEY_RECOVERABLE_ERROR" }); + } + if (response.status === "EMAIL_ALREADY_EXISTS_ERROR") { + dispatch({ type: "setError", error: "WEBAUTHN_EMAIL_ALREADY_EXISTS_ERROR" }); + } + if (response.status === "WEBAUTHN_NOT_SUPPORTED") { + dispatch({ type: "setError", error: "WEBAUTHN_NOT_SUPPORTED_ERROR" }); + } + return [2 /*return*/, response]; + } + }); + }); + }, + [state] + ); + var onSuccess = React__namespace.useCallback( + function () { + var redirectToPath = genericComponentOverrideContext.getRedirectToPathFromURL(); + return types.Session.getInstanceOrThrow() + .validateGlobalClaimsAndHandleSuccessRedirection( + undefined, + recipe.recipeID, + redirectToPath, + userContext, + navigate + ) + .catch(rethrowInRender); + }, + [recipe, userContext, navigate] + ); + return React__namespace.useMemo( + function () { + return { + onSignIn: function () { + return genericComponentOverrideContext.__awaiter(_this, void 0, void 0, function () { + var fieldUpdates, _a, result, generalError, fetchError; + return genericComponentOverrideContext.__generator(this, function (_b) { + switch (_b.label) { + case 0: + fieldUpdates = []; + _b.label = 1; + case 1: + _b.trys.push([1, 3, , 4]); + return [ + 4 /*yield*/, + genericComponentOverrideContext.handleCallAPI({ + apiFields: [], + fieldUpdates: fieldUpdates, + callAPI: callSignInAPI, + }), + ]; + case 2: + (_a = _b.sent()), + (result = _a.result), + (generalError = _a.generalError), + (fetchError = _a.fetchError); + if (generalError !== undefined) { + dispatch({ type: "setError", error: generalError.message }); + } else if (fetchError !== undefined) { + dispatch({ type: "setError", error: "Failed to fetch from upstream" }); + } else if (result.status === "OK") { + dispatch({ type: "setError", error: undefined }); + void onSuccess(); + } + return [3 /*break*/, 4]; + case 3: + _b.sent(); + dispatch({ type: "setError", error: "SOMETHING_WENT_WRONG_ERROR" }); + return [3 /*break*/, 4]; + case 4: + return [2 /*return*/]; + } + }); + }); + }, + onSignUp: function (email) { + return genericComponentOverrideContext.__awaiter(_this, void 0, void 0, function () { + var fieldUpdates, _a, result, generalError, fetchError, e_2; + return genericComponentOverrideContext.__generator(this, function (_b) { + switch (_b.label) { + case 0: + fieldUpdates = []; + _b.label = 1; + case 1: + _b.trys.push([1, 3, , 4]); + return [ + 4 /*yield*/, + genericComponentOverrideContext.handleCallAPI({ + apiFields: [], + fieldUpdates: fieldUpdates, + callAPI: function () { + var params = []; + for (var _i = 0; _i < arguments.length; _i++) { + params[_i] = arguments[_i]; + } + return callSignUpAPI.apply( + void 0, + genericComponentOverrideContext.__spreadArray( + [email], + params, + false + ) + ); + }, + }), + ]; + case 2: + (_a = _b.sent()), + (result = _a.result), + (generalError = _a.generalError), + (fetchError = _a.fetchError); + if (generalError !== undefined) { + dispatch({ type: "setError", error: generalError.message }); + } else if (fetchError !== undefined) { + dispatch({ type: "setError", error: "WEBAUTHN_PASSKEY_RECOVERABLE_ERROR" }); + } else if ( + (result === null || result === void 0 ? void 0 : result.status) === "OK" + ) { + dispatch({ type: "setError", error: undefined }); + void onSuccess(); + } + return [3 /*break*/, 4]; + case 3: + e_2 = _b.sent(); + dispatch({ type: "setError", error: "SOMETHING_WENT_WRONG_ERROR" }); + console.error("error", e_2); + return [3 /*break*/, 4]; + case 4: + return [2 /*return*/]; + } + }); + }); + }, + onSignOutClicked: function () { + return genericComponentOverrideContext.__awaiter(_this, void 0, void 0, function () { + return genericComponentOverrideContext.__generator(this, function (_a) { + switch (_a.label) { + case 0: + return [ + 4 /*yield*/, + types.Session.getInstanceOrThrow().signOut({ userContext: userContext }), + ]; + case 1: + _a.sent(); + return [ + 4 /*yield*/, + uiEntry.redirectToAuth({ redirectBack: false, navigate: navigate }), + ]; + case 2: + _a.sent(); + return [2 /*return*/]; + } + }); + }); + }, + onBackButtonClicked: function () { + return genericComponentOverrideContext.__awaiter(_this, void 0, void 0, function () { + return genericComponentOverrideContext.__generator(this, function (_a) { + // If we don't have navigate available this would mean we are using react-router-dom, so we use window's history + if (navigate === undefined) { + return [ + 2 /*return*/, + windowHandler.WindowHandlerReference.getReferenceOrThrow() + .windowHandler.getWindowUnsafe() + .history.back(), + ]; + } + // If we do have navigate and goBack function on it this means we are using react-router-dom v5 or lower + if ("goBack" in navigate) { + return [2 /*return*/, navigate.goBack()]; + } + // If we reach this code this means we are using react-router-dom v6 + return [2 /*return*/, navigate(-1)]; + }); + }); + }, + onRecoverAccountClick: function () { + void recipe.redirect( + { action: "SEND_RECOVERY_EMAIL", tenantIdFromQueryParams: "" }, + navigate, + {}, + userContext + ); + }, + recipeImplementation: recipeImplementation, + config: recipe.config, + }; + }, + [recipeImplementation, state, recipe, userContext, navigate] + ); +} +var MFAFeature = function (props) { + var recipeComponentOverrides = props.useComponentOverrides(); + return jsxRuntime.jsx( + uiEntry.ComponentOverrideContext.Provider, + genericComponentOverrideContext.__assign( + { value: recipeComponentOverrides }, + { + children: jsxRuntime.jsx( + uiEntry.FeatureWrapper, + genericComponentOverrideContext.__assign( + { + useShadowDom: genericComponentOverrideContext.SuperTokens.getInstanceOrThrow().useShadowDom, + defaultStore: defaultTranslationsWebauthn, + }, + { + children: jsxRuntime.jsx( + MFAFeatureInner, + genericComponentOverrideContext.__assign({}, props) + ), + } + ) + ), + } + ) + ); +}; +var MFAFeatureInner = function (props) { + var userContext = uiEntry.useUserContext(); + var _a = useFeatureReducer(), + state = _a[0], + dispatch = _a[1]; + var childProps = useChildProps$2( + props.recipe, + props.recipe.webJSRecipe, + state, + dispatch, + userContext, + props.navigate + ); + useOnLoad(props, props.recipe.webJSRecipe, dispatch, userContext); + return jsxRuntime.jsxs(React.Fragment, { + children: [ + props.children === undefined && + jsxRuntime.jsx( + MFAThemeWrapper, + genericComponentOverrideContext.__assign({}, childProps, { + featureState: state, + dispatch: dispatch, + }) + ), + props.children && + React__namespace.Children.map(props.children, function (child) { + if (React__namespace.isValidElement(child)) { + return React__namespace.cloneElement( + child, + genericComponentOverrideContext.__assign( + genericComponentOverrideContext.__assign({}, childProps), + { featureState: state, dispatch: dispatch } + ) + ); + } + return child; + }), + ], + }); +}; +function useOnLoad(props, recipeImplementation, dispatch, userContext) { + var _this = this; + var fetchMFAInfo = React__namespace.useCallback( + function () { + return genericComponentOverrideContext.__awaiter(_this, void 0, void 0, function () { + return genericComponentOverrideContext.__generator(this, function (_a) { + return [ + 2 /*return*/, + recipe.MultiFactorAuth.getInstanceOrThrow().webJSRecipe.resyncSessionAndFetchMFAInfo({ + userContext: userContext, + }), + ]; + }); + }); + }, + [userContext] + ); + var handleLoadError = React__namespace.useCallback( + function () { + dispatch({ type: "setError", accessDenied: true, error: "SOMETHING_WENT_WRONG_ERROR_RELOAD" }); + }, + [dispatch] + ); + var onLoad = React__namespace.useCallback( + function (mfaInfo) { + return genericComponentOverrideContext.__awaiter(_this, void 0, void 0, function () { + var error, + errorQueryParam, + doSetup, + stepUp, + redirectToPath, + showBackButton, + mfaInfoEmails, + email, + canRegisterPasskey, + hasRegisteredPassKey, + browserSupportsWebauthnResponse, + deviceSupported; + return genericComponentOverrideContext.__generator(this, function (_b) { + switch (_b.label) { + case 0: + error = undefined; + errorQueryParam = genericComponentOverrideContext.getQueryParams("error"); + doSetup = genericComponentOverrideContext.getQueryParams("setup"); + stepUp = genericComponentOverrideContext.getQueryParams("stepUp"); + if (errorQueryParam !== null) { + error = "SOMETHING_WENT_WRONG_ERROR"; + } + if (!(mfaInfo.factors.next.length === 0 && stepUp !== "true" && doSetup !== "true")) + return [3 /*break*/, 4]; + redirectToPath = genericComponentOverrideContext.getRedirectToPathFromURL(); + _b.label = 1; + case 1: + _b.trys.push([1, 3, , 4]); + return [ + 4 /*yield*/, + types.Session.getInstanceOrThrow().validateGlobalClaimsAndHandleSuccessRedirection( + undefined, + props.recipe.recipeID, + redirectToPath, + userContext, + props.navigate + ), + ]; + case 2: + _b.sent(); + return [3 /*break*/, 4]; + case 3: + _b.sent(); + // If we couldn't redirect to EV (or an unknown claim validation failed or somehow the redirection threw an error) + // we fall back to showing the something went wrong error + dispatch({ + type: "setError", + accessDenied: true, + error: "SOMETHING_WENT_WRONG_ERROR_RELOAD", + }); + return [2 /*return*/]; + case 4: + showBackButton = + mfaInfo.factors.next.length === 0 || + recipe.getAvailableFactors( + mfaInfo.factors, + undefined, + recipe.MultiFactorAuth.getInstanceOrThrow(), + userContext + ).length !== 1; + mfaInfoEmails = mfaInfo.emails[types.FactorIds.WEBAUTHN]; + email = mfaInfoEmails ? mfaInfoEmails[0] : undefined; + canRegisterPasskey = mfaInfo.factors.allowedToSetup.includes(types.FactorIds.WEBAUTHN); + hasRegisteredPassKey = mfaInfo.factors.alreadySetup.includes(types.FactorIds.WEBAUTHN); + if (!hasRegisteredPassKey && !canRegisterPasskey) { + dispatch({ + type: "setError", + accessDenied: true, + error: "SOMETHING_WENT_WRONG_ERROR", + }); + } + return [ + 4 /*yield*/, + props.recipe.webJSRecipe.doesBrowserSupportWebAuthn({ + userContext: userContext, + }), + ]; + case 5: + browserSupportsWebauthnResponse = _b.sent(); + deviceSupported = + browserSupportsWebauthnResponse.status === "OK" && + (browserSupportsWebauthnResponse === null || browserSupportsWebauthnResponse === void 0 + ? void 0 + : browserSupportsWebauthnResponse.browserSupportsWebauthn); + dispatch({ + type: "load", + canRegisterPasskey: canRegisterPasskey, + hasRegisteredPassKey: hasRegisteredPassKey, + error: error, + showBackButton: showBackButton, + email: email, + deviceSupported: deviceSupported, + }); + return [2 /*return*/]; + } + }); + }); + }, + [dispatch, recipeImplementation, props.recipe, userContext] + ); + genericComponentOverrideContext.useOnMountAPICall(fetchMFAInfo, onLoad, handleLoadError); +} + +/* 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. @@ -212,100 +1431,41 @@ function MultipleDevicesIcon() { * License for the specific language governing permissions and limitations * under the License. */ -function SecurityIcon() { - return jsxRuntime.jsx( - "svg", - genericComponentOverrideContext.__assign( - { width: "16", height: "20", viewBox: "0 0 16 20", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, - { - children: jsxRuntime.jsx("path", { - "fill-rule": "evenodd", - "clip-rule": "evenodd", - d: "M7.74971 0.0115695C7.7268 0.0212227 7.6556 0.0538204 7.59151 0.0839829C7.52743 0.114175 7.46681 0.13888 7.45681 0.13888C7.44681 0.13888 7.39926 0.157687 7.35112 0.180661C7.30298 0.203635 7.16983 0.263813 7.05525 0.314426C6.94066 0.36501 6.8219 0.418234 6.79135 0.432699C6.76079 0.447164 6.68579 0.480759 6.62467 0.507342C6.37589 0.615551 5.77415 0.875893 5.51353 0.988063C5.36074 1.05385 5.19824 1.12541 5.1524 1.14715C4.95626 1.24013 4.84114 1.29074 4.60127 1.3895C4.46132 1.44713 4.33432 1.50388 4.31904 1.51555C4.30376 1.52726 4.18501 1.58004 4.05514 1.63283C3.92528 1.68558 3.80713 1.74074 3.79263 1.75539C3.7781 1.77 3.75035 1.78197 3.73096 1.78197C3.71155 1.78197 3.62965 1.8133 3.54901 1.85156C3.46834 1.88985 3.31484 1.95737 3.20789 2.00158C3.10094 2.0458 3.00094 2.0921 2.98566 2.10442C2.97038 2.11678 2.92663 2.13667 2.88843 2.14864C2.85024 2.16061 2.76899 2.1935 2.70787 2.22179C2.64676 2.25004 2.51528 2.30708 2.41569 2.34851C2.31611 2.38997 2.19735 2.44443 2.1518 2.46951C2.10624 2.4946 2.05891 2.5152 2.0466 2.51531C2.01979 2.51552 1.79481 2.61622 1.68475 2.67725C1.6415 2.70125 1.595 2.72088 1.58142 2.72088C1.5678 2.72088 1.48839 2.75388 1.40488 2.79423C1.32138 2.83457 1.24477 2.86758 1.23463 2.86758C1.21565 2.86758 1.15515 2.89487 0.883142 3.02608C0.796222 3.06801 0.716997 3.10231 0.70708 3.10231C0.697135 3.10231 0.610744 3.14057 0.515101 3.18734C0.287482 3.2986 0.144588 3.43447 0.0647523 3.61557L0 3.76248L0.000472403 6.16843C0.000722412 7.4917 0.0127228 8.69321 0.0271122 8.83845C0.0954757 9.52819 0.136449 9.87641 0.163506 9.99741C0.179729 10.07 0.210841 10.2351 0.232619 10.3642C0.282788 10.6612 0.357707 11.0257 0.380402 11.083C0.390014 11.1072 0.419598 11.2129 0.44621 11.3177C0.547602 11.7175 0.588604 11.8615 0.609132 11.8899C0.620799 11.906 0.645578 11.9851 0.66419 12.0656C0.682802 12.146 0.707886 12.2252 0.719914 12.2416C0.73197 12.2579 0.750693 12.3105 0.761554 12.3583C0.782722 12.4515 0.86303 12.6589 0.930394 12.7945C0.953117 12.8402 0.9717 12.8917 0.9717 12.9091C0.9717 12.9899 1.65945 14.4387 1.72056 14.4866C1.73087 14.4946 1.78167 14.5805 1.83348 14.6773C1.88529 14.7741 1.93793 14.8665 1.95043 14.8827C1.96296 14.8988 2.01304 14.978 2.06171 15.0587C2.11041 15.1394 2.15699 15.212 2.16524 15.2201C2.17349 15.2282 2.21769 15.294 2.2635 15.3664C2.35014 15.5034 2.68262 15.9729 2.73396 16.0309C2.75018 16.0491 2.80718 16.1216 2.86066 16.1918C3.36731 16.8573 4.44152 17.931 4.99998 18.3302C5.03384 18.3544 5.11323 18.4147 5.17643 18.4643C5.34457 18.5961 5.63792 18.8076 5.74964 18.8775C5.80312 18.911 5.9301 18.9924 6.03185 19.0585C6.23508 19.1903 6.59345 19.3957 6.91635 19.5653C7.09641 19.6599 7.19069 19.706 7.51359 19.8574C7.97342 20.073 8.08467 20.06 8.79142 19.7087C9.03984 19.5852 9.3118 19.4446 9.40788 19.3899C9.72578 19.2089 9.98768 19.0479 9.99515 19.0287C10.0003 19.0157 10.0167 19.0051 10.0316 19.0051C10.0698 19.0051 10.6872 18.5723 11.0249 18.3089C12.0679 17.4951 13.1408 16.3045 13.8714 15.1499C14.1104 14.7723 14.3209 14.4174 14.3702 14.3091C14.3808 14.2857 14.4219 14.2071 14.4615 14.1345C14.5716 13.9323 14.8431 13.3536 14.9432 13.1075C14.9925 12.9865 15.0499 12.8479 15.0709 12.7995C15.1328 12.6565 15.3046 12.1685 15.3628 11.9706C15.3924 11.8697 15.4429 11.6981 15.4749 11.5892C15.6687 10.9305 15.8341 10.0872 15.9023 9.41059C15.9243 9.19274 15.9553 8.90077 15.9711 8.76178C15.9889 8.60642 16 7.599 16 6.14675V3.78445L15.9332 3.62493C15.8529 3.4331 15.7179 3.30153 15.4843 3.18734C15.3887 3.14057 15.3023 3.10231 15.2924 3.10231C15.2824 3.10231 15.2032 3.06801 15.1163 3.02608C14.8443 2.89487 14.7838 2.86758 14.7648 2.86758C14.7547 2.86758 14.6781 2.83457 14.5946 2.79423C14.5111 2.75388 14.4316 2.72088 14.418 2.72088C14.4044 2.72088 14.3579 2.70125 14.3147 2.67725C14.2046 2.61622 13.9797 2.51552 13.9528 2.51531C13.9405 2.5152 13.8932 2.4946 13.8476 2.46951C13.8021 2.44443 13.6833 2.38997 13.5837 2.34851C13.4842 2.30708 13.3527 2.25004 13.2916 2.22179C13.2305 2.1935 13.1492 2.16061 13.111 2.14864C13.0728 2.13667 13.0291 2.11678 13.0138 2.10442C12.9985 2.0921 12.8985 2.04577 12.7916 2.00153C12.6846 1.95728 12.5471 1.89754 12.486 1.86882C12.4249 1.84006 12.2311 1.75533 12.0554 1.68048C11.8797 1.60563 11.6985 1.52623 11.6526 1.50402C11.5737 1.46573 11.1657 1.28666 10.9859 1.21141C10.9401 1.19219 10.8714 1.16126 10.8332 1.14263C10.795 1.12403 10.6387 1.05473 10.4859 0.98865C10.1118 0.826835 9.51158 0.566845 9.37477 0.507342C9.31366 0.480759 9.23865 0.447164 9.2081 0.432699C9.17754 0.418234 9.05879 0.36501 8.9442 0.314426C8.82961 0.263813 8.69647 0.203635 8.64833 0.180661C8.60019 0.157687 8.55263 0.13888 8.54263 0.13888C8.53263 0.13888 8.46577 0.109685 8.39404 0.0739775C8.29754 0.0259465 8.20223 0.00710975 8.0275 0.00153498C7.89764 -0.00260208 7.77263 0.00191639 7.74971 0.0115695ZM8.04872 1.66343C8.07525 1.67895 8.1907 1.73276 8.30529 1.78305C8.41988 1.83331 8.55302 1.89326 8.60116 1.91623C8.6493 1.93921 8.69741 1.95801 8.70808 1.95801C8.71875 1.95801 8.76686 1.97682 8.815 1.99979C8.86314 2.02277 8.99629 2.08295 9.11087 2.13356C9.22546 2.18414 9.34421 2.23737 9.37477 2.25183C9.44263 2.28396 9.57897 2.34338 9.88868 2.47573C10.0185 2.53125 10.1642 2.59591 10.2123 2.61942C10.2605 2.64295 10.3081 2.66219 10.3182 2.66219C10.3283 2.66219 10.4077 2.69649 10.4946 2.73842C10.7666 2.86963 10.8271 2.89692 10.8461 2.89692C10.8562 2.89692 10.9328 2.92993 11.0163 2.97027C11.0998 3.01062 11.1792 3.04363 11.1929 3.04363C11.2064 3.04363 11.2529 3.06325 11.2962 3.08726C11.4063 3.14828 11.6312 3.24898 11.658 3.24919C11.6704 3.2493 11.7177 3.2699 11.7632 3.29499C11.8088 3.32008 11.9276 3.37453 12.0271 3.41599C12.1267 3.45742 12.2582 3.51446 12.3193 3.54271C12.3804 3.571 12.4617 3.60389 12.4999 3.61586C12.5381 3.62783 12.5818 3.64772 12.5971 3.66008C12.6124 3.6724 12.7124 3.7187 12.8193 3.76292C12.9263 3.80713 13.0798 3.87465 13.1605 3.91294C13.2411 3.9512 13.323 3.98253 13.3424 3.98253C13.3618 3.98253 13.3896 3.9945 13.4041 4.00912C13.4186 4.02376 13.5367 4.07892 13.6666 4.13167C13.7965 4.18446 13.9152 4.23712 13.9305 4.24871C13.9458 4.2603 14.0645 4.31312 14.1944 4.36608C14.3242 4.41904 14.4461 4.47487 14.4652 4.49013C14.5184 4.53262 14.5127 7.38484 14.4575 8.33965C14.4131 9.10897 14.3956 9.27967 14.3074 9.80669C14.2208 10.3237 14.167 10.5956 14.1417 10.6439C14.1287 10.6686 14.1095 10.7346 14.0989 10.7906C14.0777 10.9029 13.9523 11.3573 13.917 11.4498C13.9046 11.4821 13.8603 11.6141 13.8185 11.7432C13.7767 11.8723 13.7263 12.0175 13.7066 12.0659C13.6869 12.1144 13.6313 12.253 13.5829 12.374C13.3094 13.0591 12.8567 13.8902 12.3467 14.6433C11.9461 15.235 11.19 16.0899 10.6375 16.576C10.4436 16.7465 9.96746 17.1328 9.8314 17.2299C9.76359 17.2783 9.69348 17.3312 9.67556 17.3473C9.65767 17.3634 9.5595 17.4295 9.45741 17.494C9.35533 17.5586 9.23002 17.6402 9.1789 17.6755C9.12779 17.7107 8.96345 17.8086 8.8137 17.8929C8.66394 17.9772 8.52266 18.0569 8.49974 18.07C8.47682 18.0831 8.35365 18.147 8.22604 18.2119C8.00886 18.3224 7.98933 18.3273 7.92047 18.2874C7.88002 18.264 7.72071 18.1795 7.56643 18.0997C7.25634 17.9394 6.95352 17.7669 6.81679 17.6728C6.76774 17.639 6.64412 17.5586 6.54203 17.494C6.43995 17.4295 6.34178 17.3634 6.32389 17.3473C6.30597 17.3312 6.23561 17.2783 6.16752 17.2299C6.03107 17.1329 5.55622 16.7476 5.36196 16.5763C4.89767 16.1667 4.34221 15.5639 3.94203 15.0353C3.80519 14.8545 3.68132 14.6911 3.66674 14.6722C3.42681 14.3603 2.83727 13.3248 2.60053 12.7995C2.52781 12.6381 2.45817 12.4858 2.44575 12.461C2.34239 12.2547 2.01299 11.2798 1.91079 10.8776C1.57408 9.55287 1.4995 8.69893 1.4995 6.16946V4.49227L1.70089 4.40944C1.81167 4.36388 1.96479 4.2971 2.04118 4.26101C2.11757 4.22492 2.26758 4.15937 2.37453 4.11533C2.48148 4.07129 2.58084 4.02338 2.59537 4.00888C2.60987 3.99439 2.63765 3.98253 2.65704 3.98253C2.67645 3.98253 2.75835 3.9512 2.83899 3.91294C2.91966 3.87465 3.07316 3.80713 3.18011 3.76292C3.28706 3.7187 3.38706 3.6724 3.40234 3.66008C3.41762 3.64772 3.46137 3.62795 3.49957 3.6161C3.53776 3.60427 3.60651 3.57707 3.65235 3.55565C3.69818 3.53426 3.81694 3.48154 3.91625 3.43852C4.01556 3.39551 4.14926 3.33524 4.21334 3.30464C4.27743 3.27404 4.33751 3.24901 4.34682 3.24901C4.36904 3.24901 4.60313 3.14277 4.70325 3.08726C4.7465 3.06325 4.793 3.04363 4.80658 3.04363C4.8202 3.04363 4.89962 3.01062 4.98312 2.97027C5.06662 2.92993 5.14323 2.89692 5.15337 2.89692C5.17235 2.89692 5.23285 2.86963 5.50486 2.73842C5.59178 2.69649 5.67303 2.66175 5.68542 2.6612C5.69784 2.66064 5.75798 2.6347 5.81909 2.60351C5.8802 2.57232 5.94082 2.54639 5.95379 2.54583C5.96676 2.54527 6.01676 2.52602 6.06491 2.50305C6.11305 2.48008 6.24619 2.4199 6.36078 2.36928C6.47536 2.3187 6.59412 2.26565 6.62467 2.25142C6.65523 2.23719 6.77399 2.18414 6.88857 2.13356C7.00316 2.08295 7.1363 2.02277 7.18444 1.99979C7.23258 1.97682 7.2807 1.95801 7.29136 1.95801C7.30203 1.95801 7.35014 1.93921 7.39828 1.91623C7.44643 1.89326 7.57957 1.83334 7.69416 1.78308C7.80874 1.73282 7.92125 1.67977 7.94417 1.66519C7.99828 1.6308 7.993 1.63089 8.04872 1.66343ZM10.5415 6.91627C10.4706 6.95341 9.84179 7.59639 8.80606 8.69069L7.18172 10.4068L6.34764 9.53438C5.60475 8.75738 5.49791 8.65636 5.37091 8.61082C5.17354 8.54008 5.00245 8.54615 4.82236 8.63027C4.63708 8.71683 4.52999 8.82272 4.43407 9.01426C4.33568 9.21069 4.3311 9.50152 4.42321 9.70066C4.51132 9.89117 6.68334 12.1849 6.86441 12.2787C7.04097 12.3701 7.31914 12.3701 7.49665 12.2788C7.59138 12.23 8.12856 11.6818 9.55947 10.1736C11.6846 7.93357 11.6387 7.98938 11.6387 7.64548C11.6387 7.33672 11.44 7.01465 11.1782 6.8991C11.0344 6.83564 10.6768 6.84526 10.5415 6.91627Z", - fill: "#1C222A", - }), - } - ) - ); -} +var RecoverAccountScreen; +(function (RecoverAccountScreen) { + RecoverAccountScreen[(RecoverAccountScreen["ContinueWithPasskey"] = 0)] = "ContinueWithPasskey"; + RecoverAccountScreen[(RecoverAccountScreen["Success"] = 1)] = "Success"; +})(RecoverAccountScreen || (RecoverAccountScreen = {})); +var SendRecoveryEmailScreen; +(function (SendRecoveryEmailScreen) { + SendRecoveryEmailScreen[(SendRecoveryEmailScreen["RecoverAccount"] = 0)] = "RecoverAccount"; + SendRecoveryEmailScreen[(SendRecoveryEmailScreen["RecoverEmailSent"] = 1)] = "RecoverEmailSent"; +})(SendRecoveryEmailScreen || (SendRecoveryEmailScreen = {})); -var blockDetails = [ - { - title: "WEBAUTHN_FEATURE_BLOCK_NO_NEED_TO_REMEMBER_PASSWORD", - subText: "WEBAUTHN_FEATURE_BLOCK_NO_NEED_TO_REMEMBER_PASSWORD_DETAIL", - icon: jsxRuntime.jsx(FingerPrintIcon, {}), - }, - { - title: "WEBAUTHN_FEATURE_BLOCK_WORKS_ON_ALL_DEVICES", - subText: "WEBAUTHN_FEATURE_BLOCK_WORKS_ON_ALL_DEVICES_DETAIL", - icon: jsxRuntime.jsx(MultipleDevicesIcon, {}), - }, - { - title: "WEBAUTHN_FEATURE_BLOCK_KEEP_ACCOUNT_SAFER", - subText: "WEBAUTHN_FEATURE_BLOCK_KEEP_ACCOUNT_SAFER_DETAIL", - icon: jsxRuntime.jsx(SecurityIcon, {}), - }, -]; -var PasskeyFeatureBlock = uiEntry.withOverride("WebauthnPasskeyFeatureBlock", function FeatureBlock(props) { - var t = translationContext.useTranslation(); - return jsxRuntime.jsxs( - "div", - genericComponentOverrideContext.__assign( - { "data-supertokens": "passkeyFeatureBlock" }, - { - children: [ - jsxRuntime.jsx( - "div", - genericComponentOverrideContext.__assign( - { "data-supertokens": "passkeyFeatureBlockIcon" }, - { children: props.icon } - ) - ), - jsxRuntime.jsxs( - "div", +var ContinueWithoutPasskey = uiEntry.withOverride( + "WebauthnContinueWithoutPasskey", + function ContinueWithoutPasskeyButton(props) { + var t = translationContext.useTranslation(); + return jsxRuntime.jsx( + "div", + genericComponentOverrideContext.__assign( + { "data-supertokens": "continueWithoutPasskey" }, + { + children: jsxRuntime.jsx( + "a", genericComponentOverrideContext.__assign( - { "data-supertokens": "passkeyFeatureBlockDetails" }, { - children: [ - jsxRuntime.jsx( - "div", - genericComponentOverrideContext.__assign( - { "data-supertokens": "passkeyFeatureBlockTitle" }, - { children: t(props.title) } - ) - ), - jsxRuntime.jsx( - "div", - genericComponentOverrideContext.__assign( - { "data-supertokens": "passkeyFeatureBlockSubText" }, - { children: t(props.subText) } - ) - ), - ], - } + onClick: props.onClick, + "data-supertokens": "formLabelLinkBtn continueWithoutPasskeyLabel", + }, + { children: t("WEBAUTHN_CONTINUE_WITHOUT_PASSKEY_BUTTON") } ) ), - ], - } - ) - ); -}); -var PasskeyFeatureBlockList = function () { - return jsxRuntime.jsx( - "div", - genericComponentOverrideContext.__assign( - { "data-supertokens": "passkeyFeatureBlocksContainer" }, - { - children: blockDetails.map(function (blockDetail) { - return jsxRuntime.jsx( - PasskeyFeatureBlock, - genericComponentOverrideContext.__assign({}, blockDetail) - ); - }), - } - ) - ); -}; + } + ) + ); + } +); var PasskeyConfirmation = uiEntry.withOverride("WebauthnPasskeyConfirmation", function PasskeyConfirmation(props) { var t = translationContext.useTranslation(); @@ -378,17 +1538,6 @@ var PasskeyConfirmation = uiEntry.withOverride("WebauthnPasskeyConfirmation", fu ); }); -var styles = - '[data-supertokens~="container"] {\n --palette-background: 255, 255, 255;\n --palette-inputBackground: 250, 250, 250;\n --palette-inputBorder: 224, 224, 224;\n --palette-primary: 28, 34, 42;\n --palette-primaryBorder: 45, 54, 68;\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: 0, 0, 0;\n --palette-textLabel: 0, 0, 0;\n --palette-textInput: 0, 0, 0;\n --palette-textPrimary: 128, 128, 128;\n --palette-textLink: 0, 122, 255;\n --palette-buttonText: 255, 255, 255;\n --palette-textGray: 54, 54, 54;\n --palette-superTokensBrandingBackground: 242, 245, 246;\n --palette-superTokensBrandingText: 173, 189, 196;\n --palette-buttonGreyedOut: 221, 221, 221;\n --palette-caution: 124, 96, 62;\n --palette-errorDark: 207, 54, 68;\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 --font-size-5: 28px;\n}\n/*\n * Default styles.\n */\n@keyframes slideTop {\n 0% {\n transform: translateY(-5px);\n }\n 100% {\n transform: translateY(0px);\n }\n}\n@keyframes swing-in-top-fwd {\n 0% {\n transform: rotateX(-100deg);\n transform-origin: top;\n opacity: 0;\n }\n 100% {\n transform: rotateX(0deg);\n transform-origin: top;\n opacity: 1;\n }\n}\n[data-supertokens~="container"] {\n font-family: "Arial", 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: 10px auto 0;\n background: rgb(var(--palette-superTokensBrandingBackground));\n color: rgb(var(--palette-superTokensBrandingText));\n text-decoration: none;\n width: -webkit-fit-content;\n width: fit-content;\n border-radius: 6px 6px 0 0;\n padding: 4px 9px;\n font-weight: 400;\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: 10px;\n margin-top: 24px;\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 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: 27.6px;\n letter-spacing: 0.58px;\n font-weight: 700;\n margin-bottom: 20px;\n color: rgb(var(--palette-textTitle));\n}\n[data-supertokens~="headerSubtitle"] {\n font-weight: 400;\n color: rgb(var(--palette-textGray));\n margin-bottom: 21px;\n}\n[data-supertokens~="headerSubtitle"][data-supertokens~="secondaryText"] {\n color: rgb(var(--palette-textGray));\n font-weight: 400;\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-2);\n font-weight: 400;\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: 400;\n letter-spacing: 0.4px;\n color: rgb(var(--palette-textPrimary));\n}\n[data-supertokens~="secondaryText"] strong {\n font-weight: 600;\n}\n[data-supertokens~="divider"] {\n margin-top: 1.5em;\n margin-bottom: 1.5em;\n border-bottom: 0.3px solid #dddddd;\n align-items: center;\n padding-bottom: 5px;\n flex: 3 3;\n}\n[data-supertokens~="headerTinyTitle"] {\n margin-top: 24px;\n font-size: var(--font-size-5);\n letter-spacing: 1.1px;\n font-weight: 700;\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 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 font-family: "Arial", sans-serif;\n background-color: transparent;\n border: 0;\n}\n[data-supertokens~="secondaryLinkWithLeftArrow"] {\n color: rgb(var(--palette-textGray));\n font-weight: 400;\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 font-family: "Arial", sans-serif;\n background-color: rgb(var(--palette-primary));\n color: rgb(var(--palette-buttonText));\n width: 100%;\n height: 34px;\n font-weight: 600;\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~="buttonGreyedOut"] {\n background-color: rgb(var(--palette-buttonGreyedOut));\n border-color: rgb(var(--palette-buttonGreyedOut));\n}\n[data-supertokens~="buttonWithIcon"] {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\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 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[data-supertokens~="delayedRender"] {\n animation-duration: 0.1s;\n animation-name: animate-fade;\n animation-delay: 0.2s;\n animation-fill-mode: backwards;\n}\n@keyframes animate-fade {\n 0% {\n opacity: 0;\n }\n 100% {\n opacity: 1;\n }\n}\n[data-supertokens~="footerLinkGroupVert"] {\n display: flex;\n flex-direction: column;\n margin-top: 10px;\n gap: 24px;\n}\n[data-supertokens~="footerLinkGroupVert"] > div {\n cursor: pointer;\n margin: 0;\n}\n[data-supertokens~="footerLinkGroupVert"] [data-supertokens~="secondaryText"] {\n font-weight: 400;\n}\n[data-supertokens~="footerLinkGroupVert"] [data-supertokens~="secondaryLinkWithLeftArrow"] {\n font-weight: 400;\n position: relative;\n left: -6px; /* half the width of the left arrow */\n}\n@media (max-width: 360px) {\n [data-supertokens~="footerLinkGroupVert"] {\n flex-direction: column;\n }\n [data-supertokens~="footerLinkGroupVert"] > div {\n margin: 0 auto;\n }\n}\n[data-supertokens~="footerLinkGroupVert"] div:only-child {\n margin-left: auto;\n margin-right: auto;\n margin-top: 14px;\n}\n[data-supertokens~="withBackButton"] {\n position: relative;\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n[data-supertokens~="dividerWithOr"] {\n padding-top: 5px;\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n color: rgb(var(--palette-textPrimary));\n}\n[data-supertokens~="dividerText"] {\n flex: 1 1;\n font-weight: 400;\n font-size: var(--font-size-1);\n}\n[data-supertokens~="formLabelWithLinkWrapper"] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n[data-supertokens~="formLabelLinkBtn"] {\n width: auto;\n margin-top: 0;\n line-height: 24px;\n font-size: var(--font-size-0);\n}\n[data-supertokens~="formLabelLinkBtn"]:hover {\n text-decoration: underline;\n}\n[data-supertokens~="formLabelLinkBtn"]:disabled {\n color: rgb(var(--palette-textPrimary));\n cursor: default;\n text-decoration: none;\n}\n[data-supertokens~="authComponentList"] {\n padding-bottom: 20px;\n}\n[data-supertokens~="authPageTitleOAuthClient"] {\n color: rgb(var(--palette-textGray));\n font-size: var(--font-size-1);\n font-weight: 400;\n margin: 10px 0 25px;\n}\n[data-supertokens~="authPageTitleOAuthClientUrl"] {\n text-decoration: none;\n}\n[data-supertokens~="authPageTitleOAuthClientLogo"] {\n width: 44px;\n height: 44px;\n margin-bottom: 10px;\n}\n[data-supertokens~="authPageTitleOAuthClient"] [data-supertokens~="authPageTitleOAuthClientName"] {\n color: rgb(var(--palette-textTitle));\n}\n[data-supertokens~="buttonWithArrow"] {\n border-radius: 6px;\n border: 1px solid #d0d5dd;\n width: 100%;\n color: rgb(var(--palette-textGray));\n display: flex;\n justify-content: center;\n align-items: center;\n gap: 5px;\n margin: 24px 0;\n min-height: 48px;\n cursor: pointer;\n}\n[data-supertokens~="buttonWithArrow"]:hover {\n background-color: rgb(var(--palette-inputBackground));\n}\n[data-supertokens~="buttonWithArrow"] [data-supertokens~="secondaryText"] {\n font-weight: 700;\n font-size: var(--font-size-2);\n color: rgb(var(--palette-textGray));\n margin: 0;\n}\n[data-supertokens~="buttonWithArrow"]:hover [data-supertokens~="secondaryLinkWithRightArrow"] ~ svg {\n position: relative;\n left: 2px;\n}\n[data-supertokens~="buttonWithArrow"]:hover [data-supertokens~="secondaryLinkWithLeftArrow"] svg {\n position: relative;\n left: -2px;\n}\n[data-supertokens~="buttonWithArrow"] [data-supertokens~="secondaryLinkWithLeftArrow"] {\n display: flex;\n align-items: center;\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 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~="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 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: 700;\n font-size: var(--font-size-0);\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: 20px;\n}\n[data-supertokens~="formRow"][data-supertokens~="hasError"] {\n padding-bottom: 0;\n}\n[data-supertokens~="formRow"]:last-child {\n padding-bottom: 0;\n}\n[data-supertokens~="sendVerifyEmailIcon"] {\n margin-top: 11px;\n}\n[data-supertokens~="primaryText"][data-supertokens~="sendVerifyEmailText"] {\n text-align: center;\n letter-spacing: 0.8px;\n color: rgb(var(--palette-textPrimary));\n}\n[data-supertokens~="secondaryLinkWithArrow"] {\n margin-top: 10px;\n margin-bottom: 30px;\n cursor: pointer;\n font-weight: 700;\n}\n[data-supertokens~="sendVerifyEmailResend"] {\n margin-top: 13px;\n font-weight: 400;\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~="resendEmailLink"] {\n display: inline-block;\n}\n[data-supertokens~="resetPasswordEmailForm"] {\n padding-bottom: 20px;\n}\n[data-supertokens~="resetPasswordPasswordForm"] {\n padding-bottom: 20px;\n}\n[data-supertokens~="webauthn"] [data-supertokens~="container"] {\n padding-top: 24px;\n}\n[data-supertokens~="continueWithPasskeyButtonWrapper"] {\n margin: 9px 0;\n}\n[data-supertokens~="continueWithPasskeyButtonNotSupported"] {\n margin-top: 10px;\n padding: 10px;\n border-radius: 6px;\n background-color: rgba(0,122,255,0.10196);\n color: rgb(var(--palette-textLink));\n text-align: center;\n font-weight: 400;\n font-size: var(--font-size-1);\n line-height: 16.1px;\n letter-spacing: 0%;\n text-align: center;\n}\n[data-supertokens~="continueWithoutPasskey"] {\n margin-top: 15px;\n}\n[data-supertokens~="continueWithoutPasskey"] [data-supertokens~="continueWithoutPasskeyLabel"] {\n font-size: var(--font-size-1);\n font-weight: 700;\n line-height: 21px;\n text-align: center;\n text-underline-position: from-font;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n color: rgb(var(--palette-textLink));\n cursor: pointer;\n}\n[data-supertokens~="signUpFormInnerContainer"] [data-supertokens~="cautionMessage"] {\n padding: 14px;\n background: rgba(255,149,0,0.10196);\n color: rgb(var(--palette-caution));\n font-size: var(--font-size-1);\n font-weight: 400;\n line-height: 20px;\n text-align: center;\n text-underline-position: from-font;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n border-radius: 6px;\n margin-bottom: 14px;\n}\n[data-supertokens~="passkeyConfirmationContainer"] [data-supertokens~="passkeyConfirmationEmailContainer"] {\n padding-bottom: 5px;\n}\n[data-supertokens~="passkeyConfirmationContainer"]\n [data-supertokens~="passkeyConfirmationEmailContainer"]\n [data-supertokens~="continueWithLabel"] {\n font-size: var(--font-size-0);\n font-weight: 400;\n line-height: 13.8px;\n text-align: center;\n text-underline-position: from-font;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n color: rgb(var(--palette-textGray));\n}\n[data-supertokens~="passkeyConfirmationContainer"]\n [data-supertokens~="passkeyConfirmationEmailContainer"]\n [data-supertokens~="enteredEmailId"] {\n font-size: var(--font-size-1);\n font-weight: 700;\n line-height: 16.1px;\n text-align: center;\n text-underline-position: from-font;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n}\n[data-supertokens~="passkeyConfirmationContainer"]\n [data-supertokens~="passkeyFeatureBlocksContainer"]\n [data-supertokens~="passkeyFeatureBlock"] {\n margin-top: 10px;\n padding: 18px;\n border-radius: 6px;\n border: 1px solid #dddddd;\n display: flex;\n}\n[data-supertokens~="passkeyConfirmationContainer"]\n [data-supertokens~="passkeyFeatureBlocksContainer"]\n [data-supertokens~="passkeyFeatureBlock"]\n [data-supertokens~="passkeyFeatureBlockDetails"] {\n margin-left: 18px;\n}\n[data-supertokens~="passkeyConfirmationContainer"]\n [data-supertokens~="passkeyFeatureBlocksContainer"]\n [data-supertokens~="passkeyFeatureBlock"]\n [data-supertokens~="passkeyFeatureBlockDetails"]\n [data-supertokens~="passkeyFeatureBlockTitle"] {\n font-size: var(--font-size-1);\n font-weight: 700;\n line-height: 16.1px;\n text-align: left;\n text-underline-position: from-font;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n color: rgb(var(--palette-textTitle));\n}\n[data-supertokens~="passkeyConfirmationContainer"]\n [data-supertokens~="passkeyFeatureBlocksContainer"]\n [data-supertokens~="passkeyFeatureBlock"]\n [data-supertokens~="passkeyFeatureBlockDetails"]\n [data-supertokens~="passkeyFeatureBlockSubText"] {\n font-size: var(--font-size-0);\n font-weight: 400;\n line-height: 13.8px;\n text-align: left;\n text-underline-position: from-font;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n color: rgb(var(--palette-textGray));\n margin-top: 9px;\n}\n[data-supertokens~="passkeyConfirmationContainer"] [data-supertokens~="passkeyConfirmationFooter"] {\n margin-top: 25px;\n}\n[data-supertokens~="somethingWentWrongContainer"]\n [data-supertokens~="somethingWentWrongErrorDetailsContainer"]\n [data-supertokens~="label"] {\n margin-top: 4px;\n font-size: var(--font-size-4);\n font-weight: 500;\n line-height: 40px;\n letter-spacing: 0.58px;\n text-align: center;\n text-underline-position: from-font;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n color: #222222;\n}\n[data-supertokens~="somethingWentWrongContainer"]\n [data-supertokens~="somethingWentWrongErrorDetailsContainer"]\n [data-supertokens~="errorDetails"] {\n font-size: var(--font-size-1);\n font-weight: 400;\n line-height: 21px;\n letter-spacing: 0.4px;\n text-align: center;\n text-underline-position: from-font;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n color: #656565;\n}\n[data-supertokens~="somethingWentWrongContainer"] [data-supertokens~="goBackButtonContainer"] {\n margin-top: 22px;\n}\n[data-supertokens~="somethingWentWrongContainer"]\n [data-supertokens~="goBackButtonContainer"]\n [data-supertokens~="errorGoBackLabel"] {\n font-size: var(--font-size-1);\n font-weight: 500;\n line-height: 21px;\n text-align: center;\n text-underline-position: from-font;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n color: #0076ff;\n}\n[data-supertokens~="passkeySignInContainer"] {\n margin-bottom: 6px;\n}\n[data-supertokens~="passkeyRecoverAccountFormContainer"]\n [data-supertokens~="passkeyRecoverAccountFormHeaderWrapper"]\n [data-supertokens~="passkeyRecoverAccountFormHeader"] {\n padding-top: 10px;\n}\n[data-supertokens~="passkeyRecoverAccountFormContainer"]\n [data-supertokens~="passkeyRecoverAccountFormHeaderWrapper"]\n [data-supertokens~="passkeyRecoverAccountFormSubHeader"] {\n width: 75%;\n margin: 0 auto;\n font-size: var(--font-size-1);\n font-weight: 400;\n line-height: 16.1px;\n text-align: center;\n text-underline-position: from-font;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n color: rgb(var(--palette-textGray));\n margin-bottom: 20px;\n}\n[data-supertokens~="passkeyRecoverAccountFormContainer"] [data-supertokens~="errorContainer"] {\n margin-bottom: 15px;\n}\n[data-supertokens~="passkeyEmailSentContainer"] [data-supertokens~="emailSentDescription"] {\n margin: 0 auto;\n font-size: var(--font-size-1);\n font-weight: 400;\n line-height: 16.1px;\n text-align: center;\n text-underline-position: from-font;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n color: rgb(var(--palette-textGray));\n}\n[data-supertokens~="passkeyEmailSentContainer"]\n [data-supertokens~="emailSentDescription"]\n [data-supertokens~="changeEmailBtn"] {\n line-height: 16.1px;\n}\n[data-supertokens~="passkeyRecoverAccountSuccessContainer"]\n [data-supertokens~="header"]\n [data-supertokens~="headerText"] {\n margin-top: 20px;\n font-size: 20px;\n font-weight: 700;\n line-height: 30px;\n text-align: center;\n text-underline-position: from-font;\n -webkit-text-decoration-skip-ink: none;\n text-decoration-skip-ink: none;\n color: rgb(var(--palette-textTitle));\n}\n'; - -var ThemeBase = function (_a) { - var children = _a.children, - userStyles = _a.userStyles; - return jsxRuntime.jsxs(React.Fragment, { - children: [children, jsxRuntime.jsxs("style", { children: [styles, userStyles.join("\n")] })], - }); -}; - /* Copyright (c) 2025, VRAI Labs and/or its affiliates. All rights reserved. * * This software is licensed under the Apache License, Version 2.0 (the @@ -603,64 +1752,6 @@ function PasskeyRecoverAccountWithTokenTheme(props) { ); } -var defaultTranslationsWebauthn = { - en: genericComponentOverrideContext.__assign( - genericComponentOverrideContext.__assign({}, genericComponentOverrideContext.defaultTranslationsCommon.en), - { - WEBAUTHN_EMAIL_CONTINUE_BUTTON: "CONTINUE", - WEBAUTHN_SIGN_UP_LABEL: "Email", - WEBAUTHN_RECOVER_ACCOUNT_LABEL: "Recover Account", - WEBAUTHN_RECOVER_ACCOUNT_SUBHEADER_LABEL: "We will send you an email to recover your account.", - WEBAUTHN_CONTINUE_WITHOUT_PASSKEY_BUTTON: "Continue without passkey", - WEBAUTHN_CREATE_A_PASSKEY_HEADER: "Create a passkey", - WEBAUTHN_CONTINUE_WITH_EMAIL_SUBTEXT: "Continue with", - WEBAUTHN_COMBO_CONTINUE_WITH_PASSKEY_BUTTON: "CONTINUE WITH PASSKEY", - WEBAUTHN_FEATURE_BLOCK_NO_NEED_TO_REMEMBER_PASSWORD: "No need to remember password", - WEBAUTHN_FEATURE_BLOCK_NO_NEED_TO_REMEMBER_PASSWORD_DETAIL: - "With passkey, you can use things like your face or fingerprint to login.", - WEBAUTHN_FEATURE_BLOCK_WORKS_ON_ALL_DEVICES: "Works on all devices", - WEBAUTHN_FEATURE_BLOCK_WORKS_ON_ALL_DEVICES_DETAIL: - "Passkey will automatically be available across all your synced devices.", - WEBAUTHN_FEATURE_BLOCK_KEEP_ACCOUNT_SAFER: "Keep your account safer", - WEBAUTHN_FEATURE_BLOCK_KEEP_ACCOUNT_SAFER_DETAIL: "Passkey offer state of the art phishing resistance.", - WEBAUTHN_PASSKEY_RECOVERABLE_ERROR: - "The request either timed out, was canceled or the device is already registered. Please try again or try using another device.", - WEBAUTHN_PASSKEY_INVALID_CREDENTIALS_ERROR: - "The passkey is invalid, please try again, possibly with a different device.", - WEBAUTHN_ERROR_GO_BACK_BUTTON_LABEL: "Go back", - WEBAUTHN_UNRECOVERABLE_ERROR: "Something went wrong", - WEBAUTHN_UNRECOVERABLE_ERROR_DETAILS: "Something went wrong with your current session. please try again.", - WEBAUTHN_EMAIL_SENT_LABEL: "Email sent", - WEBAUTHN_EMAIL_SENT_LABEL_PRE_EMAIL: "Account recovery email has been sent to ", - WEBAUTHN_EMAIL_SENT_LABEL_POST_EMAIL: ", if it exists in our system.", - WEBAUTHN_RESEND_OR_CHANGE_EMAIL_LABEL: "Resend or change email", - WEBAUTHN_ACCOUNT_RECOVERY_NOT_ALLOWED_LABEL: "Account Recovery is not allowed, please contact support.", - WEBAUTHN_ACCOUNT_RECOVERY_GENERAL_ERROR: - "Something went wrong while trying to send recover account token, please try again.", - WEBAUTHN_ACCOUNT_RECOVERY_SUCCESSFUL_LABEL: "Account recovered successfully!", - WEBAUTHN_ACCOUNT_RECOVERY_TOKEN_INVALID_ERROR: - "The token used for recovering the account is invalid. Please try with a different token or request a new one.", - WEBAUTHN_ACCOUNT_RECOVERY_INVALID_EMAIL_ERROR: - "The email used is invalid. Please try with a different email ID or reach out to support.", - WEBAUTHN_ACCOUNT_RECOVERY_INVALID_GENERATED_OPTIONS_ERROR: "Failed to recover account, please try again.", - WEBAUTHN_ACCOUNT_RECOVERY_FETCH_ERROR: - "Something went wrong, please refresh the page or reach out to support.", - WEBAUTHN_SIGN_UP_CAUTION_MESSAGE_LABEL: - "Make sure your email is correct—we’ll use it to help you recover your account.", - WEBAUTHN_ACCOUNT_RECOVERY_INVALID_CREDENTIALS_ERROR: - "The passkey is invalid, please try again, possibly with a different device.", - WEBAUTHN_ACCOUNT_RECOVERY_GENERATED_OPTIONS_NOT_FOUND_ERROR: "Failed to recover account, please try again.", - WEBAUTHN_ACCOUNT_RECOVERY_INVALID_AUTHENTICATOR_ERROR: "Invalid authenticator, please try again.", - WEBAUTHN_EMAIL_ALREADY_EXISTS_ERROR: "Email already exists, please sign in instead.", - WEBAUTHN_NOT_SUPPORTED_ERROR: - "Passkey is not supported on your browser, please try with a different browser.", - WEBAUTHN_PASSKEY_NOT_SUPPORTED_BY_BROWSER: - "Your browser does not support passkey flow, please try in a different browser.", - WEBAUTHN_EMAIL_INPUT_NOT_POPULATED_ERROR: "Please enter your email to continue.", - } - ), -}; - var RecoverAccountUsingToken = function (props) { var token = genericComponentOverrideContext.getQueryParams("token"); var userContext; @@ -1069,7 +2160,7 @@ var WebauthnRecoverAccountForm = uiEntry.withOverride("WebauthnRecoverAccountFor ? void 0 : _a.value; if (email === undefined) { - throw new STGeneralError__default.default("GENERAL_ERROR_EMAIL_UNDEFINED"); + throw new STGeneralError__default$1.default("GENERAL_ERROR_EMAIL_UNDEFINED"); } return [ 4 /*yield*/, @@ -1331,37 +2422,6 @@ var SendRecoveryEmailForm = function (props) { ); }; -/* Copyright (c) 2025, 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. - */ -function PasskeyIcon() { - return jsxRuntime.jsx( - "svg", - genericComponentOverrideContext.__assign( - { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, - { - children: jsxRuntime.jsx("path", { - "fill-rule": "evenodd", - "clip-rule": "evenodd", - d: "M5.23974 0.00426122C5.20971 0.00917398 5.11635 0.023531 5.03227 0.0361517C4.26872 0.150797 3.53667 0.527026 2.99234 1.08462C1.7778 2.32876 1.67594 4.23877 2.75137 5.60384C3.47855 6.52688 4.6728 7.04702 5.85871 6.9572C6.17441 6.93329 6.23003 6.92444 6.5173 6.85235C7.49851 6.60612 8.30571 5.98307 8.79333 5.09562C9.19012 4.37346 9.30287 3.48404 9.1018 2.66238C8.85299 1.64555 8.10397 0.756337 7.12878 0.320054C6.63453 0.0989376 6.24276 0.0133032 5.67651 0.00256712C5.46631 -0.00143509 5.26976 -0.000672722 5.23974 0.00426122ZM10.6557 5.27343C10.0527 5.37376 9.55624 5.62311 9.14615 6.03156C8.75205 6.42412 8.50113 6.87791 8.36981 7.43553C8.33814 7.56998 8.33022 7.69015 8.33135 8.01787C8.33214 8.24497 8.34291 8.46891 8.35529 8.51549L8.37778 8.6002L7.45649 9.50016C6.69978 10.2393 6.51909 10.4271 6.44506 10.5509C6.14071 11.0599 6.15283 11.626 6.47843 12.1093C6.65032 12.3645 6.88539 12.5333 7.22706 12.6471C7.41015 12.7081 7.882 12.7037 8.08645 12.6391C8.4564 12.5223 8.46699 12.5139 9.49199 11.5255L10.4357 10.6155L10.6931 10.6508C11.2021 10.7205 11.6431 10.6746 12.1298 10.5012C12.6661 10.3101 13.0696 10.0101 13.4499 9.51964C13.7248 9.16523 13.8805 8.83599 13.9642 8.43238C14.0168 8.17874 14.0105 7.6749 13.9514 7.41044C13.7089 6.3251 12.8932 5.543 11.7477 5.29734C11.5289 5.25043 10.8794 5.2362 10.6557 5.27343ZM11.3837 7.50376C11.6039 7.58029 11.7631 7.85197 11.707 8.05537C11.6436 8.28527 11.4241 8.46336 11.2057 8.46209C11.1131 8.46156 10.9524 8.39644 10.877 8.32889C10.6068 8.08705 10.6618 7.67956 10.9856 7.5231C11.1211 7.45762 11.235 7.45209 11.3837 7.50376ZM4.91216 7.91218C4.22601 8.00179 3.70343 8.15612 3.11079 8.44415C1.63725 9.16034 0.531334 10.5122 0.15953 12.0518C0.0539832 12.4889 -0.0159007 13.1132 0.0031208 13.4491C0.0172068 13.6976 0.126772 13.8739 0.322949 13.9636C0.397419 13.9977 0.734237 14 5.63283 14H10.8632L10.952 13.9539C11.078 13.8885 11.1898 13.7597 11.2349 13.6282C11.2674 13.5334 11.2714 13.4709 11.2602 13.2388C11.239 12.8009 11.1878 12.4787 11.0683 12.0307C11.0094 11.8095 10.9775 11.7616 10.895 11.77C10.8442 11.7752 10.6791 11.9233 10.0829 12.4983C9.35956 13.1959 9.19625 13.338 8.96323 13.4727C8.39003 13.8042 7.595 13.8892 6.92132 13.6911C6.71145 13.6294 6.36331 13.458 6.19271 13.3324C5.60763 12.9016 5.24956 12.2772 5.19726 11.5966C5.15424 11.0364 5.27535 10.5421 5.56898 10.0796C5.67022 9.92014 5.80553 9.77737 6.50092 9.0963C7.2706 8.34249 7.31441 8.29544 7.31441 8.22274C7.31441 8.12885 7.27373 8.1002 7.07419 8.05359C6.30527 7.874 5.57177 7.82603 4.91216 7.91218Z", - fill: "white", - }), - } - ) - ); -} - var ContinueWithPasskey = function (_a) { var continueWithPasskeyClicked = _a.continueWithPasskeyClicked, isLoading = _a.isLoading, @@ -1381,7 +2441,7 @@ var ContinueWithPasskey = function (_a) { label: "WEBAUTHN_COMBO_CONTINUE_WITH_PASSKEY_BUTTON", disabled: !isPasskeySupported, isGreyedOut: !isPasskeySupported, - icon: PasskeyIcon, + icon: componentOverrideContext.PasskeyIcon, }), !isPasskeySupported && jsxRuntime.jsx(PasskeyNotSupportedError, {}), ], @@ -1900,10 +2960,10 @@ var SignUpFormInner = uiEntry.withOverride("WebauthnPasskeySignUpForm", function ? void 0 : _a.value; if (email === undefined) { - throw new STGeneralError__default$1.default("GENERAL_ERROR_EMAIL_UNDEFINED"); + throw new STGeneralError__default.default("GENERAL_ERROR_EMAIL_UNDEFINED"); } if (email === "") { - throw new STGeneralError__default$1.default("EMAIL_INPUT_NOT_POPULATED_ERROR"); + throw new STGeneralError__default.default("EMAIL_INPUT_NOT_POPULATED_ERROR"); } // We do not want the form to make the API call since we have // an intermediary step here so we will just mock an OK status @@ -2450,6 +3510,18 @@ var WebauthnPreBuiltUI = /** @class */ (function (_super) { }, recipeID: componentOverrideContext.Webauthn.RECIPE_ID, }; + var normalisedFullPathForMFA = _this.recipeInstance.config.appInfo.websiteBasePath.appendPath( + new NormalisedURLPath__default.default(componentOverrideContext.DEFAULT_WEBAUTHN_MFA_PATH) + ); + features[normalisedFullPathForMFA.getAsStringDangerous()] = { + matches: genericComponentOverrideContext.matchRecipeIdUsingQueryParams( + _this.recipeInstance.config.recipeId + ), + component: function (props) { + return _this.getFeatureComponent("webauthn-mfa", props, useComponentOverrides); + }, + recipeID: componentOverrideContext.Webauthn.RECIPE_ID, + }; } return features; }; @@ -2487,6 +3559,21 @@ var WebauthnPreBuiltUI = /** @class */ (function (_super) { } ) ); + } else if (componentName === "webauthn-mfa") { + return jsxRuntime.jsx( + uiEntry.UserContextWrapper, + genericComponentOverrideContext.__assign( + { userContext: props.userContext }, + { + children: jsxRuntime.jsx( + MFAFeature, + genericComponentOverrideContext.__assign({ recipe: _this.recipeInstance }, props, { + useComponentOverrides: useComponentOverrides, + }) + ), + } + ) + ); } throw new Error("Should never come here."); }; diff --git a/lib/ts/components/assets/passkeyIcon.tsx b/lib/ts/components/assets/passkeyIcon.tsx index 47804db70..cbd97a99e 100644 --- a/lib/ts/components/assets/passkeyIcon.tsx +++ b/lib/ts/components/assets/passkeyIcon.tsx @@ -17,10 +17,10 @@ export default function PasskeyIcon(): JSX.Element { return ( ); diff --git a/lib/ts/recipe/multifactorauth/components/themes/translations.ts b/lib/ts/recipe/multifactorauth/components/themes/translations.ts index f4dd3e010..fc32665b7 100644 --- a/lib/ts/recipe/multifactorauth/components/themes/translations.ts +++ b/lib/ts/recipe/multifactorauth/components/themes/translations.ts @@ -16,6 +16,9 @@ export const defaultTranslationsMultiFactorAuth = { TOTP_MFA_NAME: "TOTP", TOTP_MFA_DESCRIPTION: "Use an authenticator app to complete the authentication request", + WEBAUTHN_MFA_NAME: "Passkeys", + WEBAUTHN_MFA_DESCRIPTION: "Use a passkey to complete the authentication request", + 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/webauthn/components/features/mfa/index.tsx b/lib/ts/recipe/webauthn/components/features/mfa/index.tsx new file mode 100644 index 000000000..597fab383 --- /dev/null +++ b/lib/ts/recipe/webauthn/components/features/mfa/index.tsx @@ -0,0 +1,393 @@ +/* Copyright (c) 2024, 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. + */ +/* + * Imports. + */ +import * as React from "react"; +import { Fragment } from "react"; +import { WindowHandlerReference } from "supertokens-web-js/utils/windowHandler"; + +import { redirectToAuth } from "../../../../.."; +import { ComponentOverrideContext } from "../../../../../components/componentOverride/componentOverrideContext"; +import FeatureWrapper from "../../../../../components/featureWrapper"; +import SuperTokens from "../../../../../superTokens"; +import { useUserContext } from "../../../../../usercontext"; +import { + getQueryParams, + getRedirectToPathFromURL, + useOnMountAPICall, + handleCallAPI, + useRethrowInRender, +} from "../../../../../utils"; +import MultiFactorAuth from "../../../../multifactorauth/recipe"; +import { FactorIds } from "../../../../multifactorauth/types"; +import { getAvailableFactors } from "../../../../multifactorauth/utils"; +import SessionRecipe from "../../../../session/recipe"; +import MFAThemeWrapper from "../../themes/mfa"; +import { defaultTranslationsWebauthn } from "../../themes/translations"; + +import type { APIFormField } from "../../../../../types"; +import type { FeatureBaseProps, UserContext, Navigate } from "../../../../../types"; +import type { FieldState } from "../../../../emailpassword/components/library/formBase"; +import type Recipe from "../../../recipe"; +import type { ComponentOverrideMap, WebAuthnMFAAction, WebAuthnMFAProps, WebAuthnMFAState } from "../../../types"; +import type { RecipeInterface } from "supertokens-web-js/recipe/webauthn"; + +export const useFeatureReducer = (): [WebAuthnMFAState, React.Dispatch] => { + return React.useReducer( + (oldState: WebAuthnMFAState, action: WebAuthnMFAAction): WebAuthnMFAState => { + switch (action.type) { + case "setError": + return { + ...oldState, + loaded: true, + error: action.error, + accessDenied: action.accessDenied || false, + }; + case "load": + return { + ...oldState, + loaded: true, + deviceSupported: action.deviceSupported, + email: action.email, + showBackButton: action.showBackButton, + canRegisterPasskey: action.canRegisterPasskey, + hasRegisteredPassKey: action.hasRegisteredPassKey, + }; + default: + return oldState; + } + }, + { + error: undefined, + deviceSupported: false, + canRegisterPasskey: false, + hasRegisteredPassKey: false, + loaded: false, + showBackButton: true, + email: undefined, + accessDenied: false, + } + ); +}; + +export function useChildProps( + recipe: Recipe, + recipeImplementation: RecipeInterface, + state: WebAuthnMFAState, + dispatch: React.Dispatch, + userContext: UserContext, + navigate?: Navigate +): Omit { + const rethrowInRender = useRethrowInRender(); + const callSignInAPI = React.useCallback( + async (_: APIFormField[], __: (id: string, value: string) => any) => { + const response = await recipeImplementation.authenticateCredentialWithSignIn({ + shouldTryLinkingWithSessionUser: true, + userContext, + }); + + switch (response.status) { + case "INVALID_CREDENTIALS_ERROR": + dispatch({ type: "setError", error: "WEBAUTHN_PASSKEY_INVALID_CREDENTIALS_ERROR" }); + break; + case "FAILED_TO_AUTHENTICATE_USER": + case "INVALID_OPTIONS_ERROR": + dispatch({ type: "setError", error: "WEBAUTHN_PASSKEY_RECOVERABLE_ERROR" }); + break; + case "WEBAUTHN_NOT_SUPPORTED": + dispatch({ type: "setError", error: "WEBAUTHN_NOT_SUPPORTED_ERROR" }); + break; + } + + return response; + }, + [recipeImplementation, userContext] + ); + + const callSignUpAPI = React.useCallback( + async (email: string, _: APIFormField[], __: (id: string, value: string) => any) => { + const response = await recipeImplementation.registerCredentialWithSignUp({ + email, + shouldTryLinkingWithSessionUser: true, + userContext, + }); + + if (response.status !== "OK") { + dispatch({ type: "setError", error: "WEBAUTHN_PASSKEY_RECOVERABLE_ERROR" }); + } + + if (response.status === "EMAIL_ALREADY_EXISTS_ERROR") { + dispatch({ type: "setError", error: "WEBAUTHN_EMAIL_ALREADY_EXISTS_ERROR" }); + } + + if (response.status === "WEBAUTHN_NOT_SUPPORTED") { + dispatch({ type: "setError", error: "WEBAUTHN_NOT_SUPPORTED_ERROR" }); + } + + return response; + }, + [state] + ); + + const onSuccess = React.useCallback(() => { + const redirectToPath = getRedirectToPathFromURL(); + + return SessionRecipe.getInstanceOrThrow() + .validateGlobalClaimsAndHandleSuccessRedirection( + undefined, + recipe.recipeID, + redirectToPath, + userContext, + navigate + ) + .catch(rethrowInRender); + }, [recipe, userContext, navigate]); + + return React.useMemo(() => { + return { + onSignIn: async () => { + const fieldUpdates: FieldState[] = []; + try { + const { result, generalError, fetchError } = await handleCallAPI({ + apiFields: [], + fieldUpdates, + callAPI: callSignInAPI, + }); + + if (generalError !== undefined) { + dispatch({ type: "setError", error: generalError.message }); + } else if (fetchError !== undefined) { + dispatch({ type: "setError", error: "Failed to fetch from upstream" }); + } else if (result.status === "OK") { + dispatch({ type: "setError", error: undefined }); + void onSuccess(); + } + } catch (e) { + dispatch({ type: "setError", error: "SOMETHING_WENT_WRONG_ERROR" }); + } + }, + onSignUp: async (email: string) => { + const fieldUpdates: FieldState[] = []; + + try { + const { result, generalError, fetchError } = await handleCallAPI({ + apiFields: [], + fieldUpdates, + callAPI: (...params) => callSignUpAPI(email, ...params), + }); + + if (generalError !== undefined) { + dispatch({ type: "setError", error: generalError.message }); + } else if (fetchError !== undefined) { + dispatch({ type: "setError", error: "WEBAUTHN_PASSKEY_RECOVERABLE_ERROR" }); + } else if (result?.status === "OK") { + dispatch({ type: "setError", error: undefined }); + void onSuccess(); + } + } catch (e) { + dispatch({ type: "setError", error: "SOMETHING_WENT_WRONG_ERROR" }); + console.error("error", e); + } + }, + onSignOutClicked: async () => { + await SessionRecipe.getInstanceOrThrow().signOut({ userContext }); + await redirectToAuth({ redirectBack: false, navigate: navigate }); + }, + onBackButtonClicked: async () => { + // If we don't have navigate available this would mean we are using react-router-dom, so we use window's history + if (navigate === undefined) { + return WindowHandlerReference.getReferenceOrThrow().windowHandler.getWindowUnsafe().history.back(); + } + // If we do have navigate and goBack function on it this means we are using react-router-dom v5 or lower + if ("goBack" in navigate) { + return navigate.goBack(); + } + // If we reach this code this means we are using react-router-dom v6 + return navigate(-1); + }, + onRecoverAccountClick: () => { + void recipe.redirect( + { action: "SEND_RECOVERY_EMAIL", tenantIdFromQueryParams: "" }, + navigate, + {}, + userContext + ); + }, + recipeImplementation: recipeImplementation, + config: recipe.config, + }; + }, [recipeImplementation, state, recipe, userContext, navigate]); +} + +export const MFAFeature: React.FC< + FeatureBaseProps<{ + recipe: Recipe; + useComponentOverrides: () => ComponentOverrideMap; + }> +> = (props) => { + const recipeComponentOverrides = props.useComponentOverrides(); + + return ( + + + + + + ); +}; + +export default MFAFeature; + +const MFAFeatureInner: React.FC< + FeatureBaseProps<{ + recipe: Recipe; + useComponentOverrides: () => ComponentOverrideMap; + }> +> = (props) => { + const userContext = useUserContext(); + const [state, dispatch] = useFeatureReducer(); + + const childProps = useChildProps( + props.recipe, + props.recipe.webJSRecipe as RecipeInterface, + state, + dispatch, + userContext, + props.navigate + )!; + + useOnLoad(props, props.recipe.webJSRecipe as RecipeInterface, dispatch, userContext); + + return ( + + {/* No custom theme, use default. */} + {props.children === undefined && ( + + )} + + {/* Otherwise, custom theme is provided, propagate props. */} + {props.children && + React.Children.map(props.children, (child) => { + if (React.isValidElement(child)) { + return React.cloneElement(child, { + ...childProps, + featureState: state, + dispatch: dispatch, + }); + } + return child; + })} + + ); +}; + +function useOnLoad( + props: React.PropsWithChildren< + { navigate?: Navigate } & { children?: React.ReactNode } & { + recipe: Recipe; + useComponentOverrides: () => ComponentOverrideMap; + } + >, + recipeImplementation: RecipeInterface, + dispatch: React.Dispatch, + userContext: UserContext +) { + const fetchMFAInfo = React.useCallback( + async () => MultiFactorAuth.getInstanceOrThrow().webJSRecipe.resyncSessionAndFetchMFAInfo({ userContext }), + [userContext] + ); + + const handleLoadError = React.useCallback(() => { + dispatch({ type: "setError", accessDenied: true, error: "SOMETHING_WENT_WRONG_ERROR_RELOAD" }); + }, [dispatch]); + + const onLoad = React.useCallback( + async (mfaInfo: Awaited>) => { + let error: string | undefined = undefined; + const errorQueryParam = getQueryParams("error"); + const doSetup = getQueryParams("setup"); + const stepUp = getQueryParams("stepUp"); + + if (errorQueryParam !== null) { + error = "SOMETHING_WENT_WRONG_ERROR"; + } + + if (mfaInfo.factors.next.length === 0 && stepUp !== "true" && doSetup !== "true") { + const redirectToPath = getRedirectToPathFromURL(); + try { + await SessionRecipe.getInstanceOrThrow().validateGlobalClaimsAndHandleSuccessRedirection( + undefined, + props.recipe.recipeID, + redirectToPath, + userContext, + props.navigate + ); + } catch { + // If we couldn't redirect to EV (or an unknown claim validation failed or somehow the redirection threw an error) + // we fall back to showing the something went wrong error + dispatch({ + type: "setError", + accessDenied: true, + error: "SOMETHING_WENT_WRONG_ERROR_RELOAD", + }); + return; + } + } + + // If the next array only has a single option, it means the we were redirected here + // automatically during the sign in process. In that case, anywhere the back button + // could go would redirect back here, making it useless. + const showBackButton = + mfaInfo.factors.next.length === 0 || + getAvailableFactors(mfaInfo.factors, undefined, MultiFactorAuth.getInstanceOrThrow(), userContext) + .length !== 1; + + const mfaInfoEmails = mfaInfo.emails[FactorIds.WEBAUTHN]; + const email = mfaInfoEmails ? mfaInfoEmails[0] : undefined; + + const canRegisterPasskey = mfaInfo.factors.allowedToSetup.includes(FactorIds.WEBAUTHN); + const hasRegisteredPassKey = mfaInfo.factors.alreadySetup.includes(FactorIds.WEBAUTHN); + if (!hasRegisteredPassKey && !canRegisterPasskey) { + dispatch({ + type: "setError", + accessDenied: true, + error: "SOMETHING_WENT_WRONG_ERROR", + }); + } + const browserSupportsWebauthnResponse = await props.recipe.webJSRecipe.doesBrowserSupportWebAuthn({ + userContext: userContext, + }); + const deviceSupported = + browserSupportsWebauthnResponse.status === "OK" && + browserSupportsWebauthnResponse?.browserSupportsWebauthn; + + dispatch({ + type: "load", + canRegisterPasskey, + hasRegisteredPassKey, + error, + showBackButton, + email, + deviceSupported, + }); + }, + [dispatch, recipeImplementation, props.recipe, userContext] + ); + + useOnMountAPICall(fetchMFAInfo, onLoad, handleLoadError); +} diff --git a/lib/ts/recipe/webauthn/components/themes/mfa/index.tsx b/lib/ts/recipe/webauthn/components/themes/mfa/index.tsx new file mode 100644 index 000000000..a495aeca1 --- /dev/null +++ b/lib/ts/recipe/webauthn/components/themes/mfa/index.tsx @@ -0,0 +1,198 @@ +import * as React from "react"; + +import { SuperTokensBranding } from "../../../../../components/SuperTokensBranding"; +import SuperTokens from "../../../../../superTokens"; +import { useTranslation } from "../../../../../translation/translationContext"; +import UserContextWrapper from "../../../../../usercontext/userContextWrapper"; +import { AccessDeniedScreen } from "../../../../session/prebuiltui"; +import { ThemeBase } from "../themeBase"; + +import { WebauthnMFALoadingScreen } from "./loadingScreen"; +import { WebauthnMFASignIn } from "./signIn"; +import { WebauthnMFASignUp } from "./signUp"; +import { WebauthnMFASignUpConfirmation } from "./signUpConfirmation"; + +import type { WebAuthnMFAProps } from "../../../types"; + +export { WebauthnMFALoadingScreen, WebauthnMFASignIn, WebauthnMFASignUp, WebauthnMFASignUpConfirmation }; + +export enum MFAScreens { + SignIn, + SignUp, + SignUpConfirmation, +} + +function MFAThemeWrapper(props: WebAuthnMFAProps): JSX.Element { + const rootStyle = SuperTokens.getInstanceOrThrow().rootStyle; + + return ( + + + + + + ); +} + +export default MFAThemeWrapper; + +export function MFATheme(props: WebAuthnMFAProps): JSX.Element { + const t = useTranslation(); + + if (!props.featureState.loaded) { + return ; + } + + if (props.featureState.accessDenied) { + return ( + + ); + } + + return ( +
+
+ +
+ +
+ ); +} + +function MFAThemeRouter(props: WebAuthnMFAProps): JSX.Element { + const { onBackButtonClicked, onSignIn } = props; + const [activeScreen, setActiveScreen] = React.useState(() => { + if (!props.featureState.hasRegisteredPassKey) { + return props.featureState.email ? MFAScreens.SignUpConfirmation : MFAScreens.SignUp; + } + return MFAScreens.SignIn; + }); + const [email, setEmail] = React.useState(""); + const signUpEmail = props.featureState.email || email; + + const onSignUpContinue = React.useCallback( + (email: string) => { + if (!props.featureState.canRegisterPasskey) { + return; + } + setActiveScreen(MFAScreens.SignUpConfirmation); + setEmail(email); + }, + [props.featureState.canRegisterPasskey] + ); + + const onRegisterPasskeyClick = React.useCallback(() => { + if (!props.featureState.canRegisterPasskey) { + return; + } + if (props.featureState.email) { + setActiveScreen(MFAScreens.SignUpConfirmation); + } else { + setActiveScreen(MFAScreens.SignUp); + } + }, [props.featureState.email, props.featureState.canRegisterPasskey]); + + const clearError = React.useCallback(() => { + props.dispatch({ type: "setError", error: undefined }); + }, [props]); + + const onError = React.useCallback( + (error: string) => { + props.dispatch({ type: "setError", error }); + }, + [props] + ); + + const onClickSignUpConfirmationBackButton = React.useCallback(() => { + if (!props.featureState.email) { + setActiveScreen(MFAScreens.SignUp); + return; + } + if (!props.featureState.hasRegisteredPassKey && !props.featureState.showBackButton) { + return; + } + if (!props.featureState.hasRegisteredPassKey) { + onBackButtonClicked(); + return; + } + setActiveScreen(MFAScreens.SignIn); + }, [ + props.featureState.email, + props.featureState.hasRegisteredPassKey, + props.featureState.showBackButton, + onBackButtonClicked, + ]); + + const showBackButtonOnSignUpConfirmation = React.useMemo(() => { + return ( + props.featureState.email !== undefined || + props.featureState.hasRegisteredPassKey || + props.featureState.showBackButton + ); + }, [props.featureState.email, props.featureState.hasRegisteredPassKey, props.featureState.showBackButton]); + + const onSignUp = React.useCallback(async () => { + await props.onSignUp(signUpEmail); + }, [props.onSignUp, signUpEmail]); + + const onFetchError = React.useCallback(() => { + onError("SOMETHING_WENT_WRONG_ERROR"); + }, [onError]); + + const onClickSignUpBackButton = React.useCallback(() => { + if (!props.featureState.hasRegisteredPassKey && !props.featureState.showBackButton) { + return; + } + if (!props.featureState.hasRegisteredPassKey) { + onBackButtonClicked(); + return; + } + setActiveScreen(MFAScreens.SignIn); + }, [props.featureState.hasRegisteredPassKey, props.featureState.showBackButton, onBackButtonClicked]); + + const showBackButtonOnSignUp = React.useMemo(() => { + return props.featureState.hasRegisteredPassKey || props.featureState.showBackButton; + }, [props.featureState.email, props.featureState.showBackButton]); + + if (activeScreen === MFAScreens.SignUp) { + return ( + + ); + } + + if (activeScreen === MFAScreens.SignUpConfirmation) { + return ( + + ); + } + + return ( + + ); +} diff --git a/lib/ts/recipe/webauthn/components/themes/mfa/loadingScreen.tsx b/lib/ts/recipe/webauthn/components/themes/mfa/loadingScreen.tsx new file mode 100644 index 000000000..7348b8295 --- /dev/null +++ b/lib/ts/recipe/webauthn/components/themes/mfa/loadingScreen.tsx @@ -0,0 +1,14 @@ +import SpinnerIcon from "../../../../../components/assets/spinnerIcon"; +import { withOverride } from "../../../../../components/componentOverride/withOverride"; + +export const WebauthnMFALoadingScreen = withOverride("WebauthnMFALoadingScreen", function WebauthnMFALoadingScreen() { + return ( +
+
+
+ +
+
+
+ ); +}); diff --git a/lib/ts/recipe/webauthn/components/themes/mfa/signIn.tsx b/lib/ts/recipe/webauthn/components/themes/mfa/signIn.tsx new file mode 100644 index 000000000..42a1753f2 --- /dev/null +++ b/lib/ts/recipe/webauthn/components/themes/mfa/signIn.tsx @@ -0,0 +1,77 @@ +import * as React from "react"; +import { Fragment } from "react"; + +import PasskeyIcon from "../../../../../components/assets/passkeyIcon"; +import { withOverride } from "../../../../../components/componentOverride/withOverride"; +import { useTranslation } from "../../../../../translation/translationContext"; +import BackButton from "../../../../emailpassword/components/library/backButton"; +import Button from "../../../../emailpassword/components/library/button"; +import GeneralError from "../../../../emailpassword/components/library/generalError"; +import { PasskeyNotSupportedError } from "../error/passkeyNotSupportedError"; + +export type MFASignInProps = { + onBackButtonClicked?: () => void; + onSignIn: () => Promise; + error: string | undefined; + deviceSupported: boolean; + canRegisterPasskey: boolean; + onRegisterPasskeyClick: () => void; +}; + +export const WebauthnMFASignIn = withOverride( + "WebauthnMFASignIn", + function WebauthnMFASignIn(props: MFASignInProps): JSX.Element { + const t = useTranslation(); + const [isLoading, setIsLoading] = React.useState(false); + + const onClick = React.useCallback(async () => { + setIsLoading(true); + await props.onSignIn(); + setIsLoading(false); + }, [props]); + + return ( + + {props.onBackButtonClicked ? ( +
+ + {t("WEBAUTHN_MFA_SIGN_IN_HEADER_TITLE")} + + {/* empty span for spacing the back button */} + +
+ ) : ( +
{t("WEBAUTHN_MFA_SIGN_IN_HEADER_TITLE")}
+ )} + +
{t("WEBAUTHN_MFA_SIGN_IN_HEADER_SUBTITLE")}
+