Skip to content

Commit efff4bc

Browse files
Add support for getting register options on load
1 parent f61ec69 commit efff4bc

File tree

4 files changed

+68
-9
lines changed

4 files changed

+68
-9
lines changed

lib/ts/recipe/webauthn/components/features/recoverAccountWithToken/index.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import type { RecoverAccountWithTokenProps } from "../../../types";
2626

2727
export const RecoverAccountUsingToken: React.FC<RecoverAccountWithTokenProps> = (props): JSX.Element => {
2828
const token = getQueryParams("token");
29-
const email = getQueryParams("email");
3029
let userContext;
3130
if (props.userContext !== undefined) {
3231
userContext = props.userContext;
@@ -42,7 +41,6 @@ export const RecoverAccountUsingToken: React.FC<RecoverAccountWithTokenProps> =
4241
token,
4342
useComponentOverride: props.useComponentOverrides,
4443
userContext,
45-
email,
4644
};
4745
const recipeComponentOverrides = props.useComponentOverrides();
4846

lib/ts/recipe/webauthn/components/themes/recoverAccountWithToken/index.tsx

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,20 @@
1313
* under the License.
1414
*/
1515

16-
import { useCallback, useState } from "react";
16+
import { useCallback, useEffect, useState } from "react";
1717

18+
import { redirectToAuth } from "../../../../..";
1819
import { SuperTokensBranding } from "../../../../../components/SuperTokensBranding";
1920
import SuperTokens from "../../../../../superTokens";
2021
import { AuthPageFooter, AuthPageHeader } from "../../../../../ui";
2122
import UserContextWrapper from "../../../../../usercontext/userContextWrapper";
2223
import { PasskeyConfirmation } from "../signUp/confirmation";
2324
import { ThemeBase } from "../themeBase";
2425

25-
import type { RecoverAccountWithTokenThemeProps } from "../../../types";
2626
import { PasskeyRecoverAccountSuccess } from "./success";
2727

28+
import type { RecoverAccountWithTokenThemeProps } from "../../../types";
29+
2830
export enum RecoverAccountScreen {
2931
ContinueWithPasskey,
3032
Success,
@@ -39,18 +41,69 @@ function PasskeyRecoverAccountWithTokenTheme(props: RecoverAccountWithTokenTheme
3941
const privacyPolicyLink = stInstance.privacyPolicyLink;
4042
const termsOfServiceLink = stInstance.termsOfServiceLink;
4143

44+
type RegisterOptions = Extract<
45+
Awaited<ReturnType<typeof props.recipeImplementation.getRegisterOptions>>,
46+
{ status: "OK" }
47+
>;
48+
const [errorMessageLabel, setErrorMessageLabel] = useState<string | null>(null);
4249
const [activeScreen, setActiveScreen] = useState<RecoverAccountScreen>(RecoverAccountScreen.ContinueWithPasskey);
50+
const [registerOptions, setRegisterOptions] = useState<RegisterOptions | null>(null);
4351

4452
const onResetFactorList = () => {
4553
throw new Error("Should never come here as we don't have back functionality");
4654
};
4755

48-
// TODO: Get the reset options as soon as the page loads and afterwards use the token
56+
// Get the reset options as soon as the page loads and afterwards use the token
4957
// with the options.
58+
const fetchAndStoreRegisterOptions = useCallback(async () => {
59+
// If the page is loaded without a valid token, we want to redirect the user
60+
// back to the sign in page.
61+
if (props.token === null) {
62+
await redirectToAuth();
63+
return;
64+
}
65+
66+
const registerOptions = await props.recipeImplementation.getRegisterOptions({
67+
userContext: props.userContext,
68+
recoverAccountToken: props.token,
69+
});
70+
if (registerOptions.status !== "OK") {
71+
switch (registerOptions.status) {
72+
case "RECOVER_ACCOUNT_TOKEN_INVALID_ERROR":
73+
setErrorMessageLabel("WEBAUTHN_ACCOUNT_RECOVERY_TOKEN_INVALID_ERROR");
74+
break;
75+
case "INVALID_EMAIL_ERROR":
76+
setErrorMessageLabel("WEBAUTHN_ACCOUNT_RECOVERY_INVALID_EMAIL_ERROR");
77+
break;
78+
case "INVALID_GENERATED_OPTIONS_ERROR":
79+
setErrorMessageLabel("WEBAUTHN_ACCOUNT_RECOVERY_INVALID_GENERATED_OPTIONS_ERROR");
80+
// TODO: Should we trigger an automatic retry here or will there
81+
// be a separate expired token related error?
82+
break;
83+
default:
84+
throw new Error("Should never come here");
85+
}
86+
87+
return;
88+
}
89+
90+
setRegisterOptions(registerOptions);
91+
}, [props]);
92+
93+
useEffect(() => {
94+
void fetchAndStoreRegisterOptions();
95+
}, []);
5096

5197
const onContinueClick = useCallback(() => {
5298
// TODO: Add support to make the network call and show the next screen based
5399
// on that result.
100+
//
101+
// We will do the following things in the order when the user clicks on the continue
102+
// button.
103+
// 1. Check if the fetched register options have expired
104+
// 2. If not expired, we can continue and use the values to register the user.
105+
// 3. If expired, we will get new registerOptions and register the user.
106+
// 4. If registration fails with a token expiry error, we should following 3rd step.
54107
setActiveScreen(RecoverAccountScreen.Success);
55108
}, [setActiveScreen]);
56109

@@ -80,6 +133,8 @@ function PasskeyRecoverAccountWithTokenTheme(props: RecoverAccountWithTokenTheme
80133
{...props}
81134
activeScreen={activeScreen}
82135
onContinueClick={onContinueClick}
136+
errorMessageLabel={errorMessageLabel}
137+
email={registerOptions?.user.name || null}
83138
/>
84139
{activeScreen !== RecoverAccountScreen.Success && (
85140
<AuthPageFooter
@@ -102,16 +157,17 @@ const RecoverAccountThemeInner = (
102157
props: RecoverAccountWithTokenThemeProps & {
103158
activeScreen: RecoverAccountScreen;
104159
onContinueClick: () => void;
160+
errorMessageLabel: string | null;
161+
email: string | null;
105162
}
106163
) => {
107164
return props.activeScreen === RecoverAccountScreen.ContinueWithPasskey ? (
108165
<PasskeyConfirmation
109166
{...props}
110167
email={props.email || ""}
111168
onContinueClick={props.onContinueClick}
112-
// errorMessageLabel={showPasskeyConfirmationError ? "WEBAUTHN_PASSKEY_RECOVERABLE_ERROR" : undefined}
169+
errorMessageLabel={props.errorMessageLabel || undefined}
113170
isLoading={false}
114-
onFetchError={() => {}}
115171
hideContinueWithoutPasskey
116172
/>
117173
) : props.activeScreen === RecoverAccountScreen.Success ? (

lib/ts/recipe/webauthn/components/themes/translations.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,10 @@ export const defaultTranslationsWebauthn = {
3232
WEBAUTHN_ACCOUNT_RECOVERY_GENERAL_ERROR:
3333
"Something went wrong while trying to send recover account token, please try again.",
3434
WEBAUTHN_ACCOUNT_RECOVERY_SUCCESSFUL_LABEL: "Account recovered successfully!",
35+
WEBAUTHN_ACCOUNT_RECOVERY_TOKEN_INVALID_ERROR:
36+
"The token used for recovering the account is invalid. Please try with a different token or request a new one.",
37+
WEBAUTHN_ACCOUNT_RECOVERY_INVALID_EMAIL_ERROR:
38+
"The email used is invalid. Please try with a different email ID or reach out to support.",
39+
WEBAUTHN_ACCOUNT_RECOVERY_INVALID_GENERATED_OPTIONS_ERROR: "Failed to recover account, please try again.",
3540
},
3641
};

lib/ts/recipe/webauthn/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ export type SignInThemeProps = SignUpThemeProps;
163163
export type SignUpFormProps = {
164164
clearError: () => void;
165165
onError: (error: string) => void;
166-
onFetchError: (error: Response) => void;
166+
onFetchError?: (error: Response) => void;
167167
error: string | undefined;
168168
recipeImplementation: RecipeImplementation;
169169
config: NormalisedConfig;
@@ -184,7 +184,7 @@ export type RecoverAccountWithTokenThemeProps = {
184184
error: string | undefined;
185185
clearError: () => void;
186186
onError: (error: string) => void;
187-
email: string | null;
187+
token: string | null;
188188
};
189189

190190
export type ContinueOnSuccessParams = {

0 commit comments

Comments
 (0)