Skip to content

Commit e3a6d63

Browse files
Add support for rendering passkey link form
1 parent 700809f commit e3a6d63

File tree

5 files changed

+180
-7
lines changed

5 files changed

+180
-7
lines changed

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

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,55 @@
1414
*/
1515

1616
import * as React from "react";
17+
18+
import { ComponentOverrideContext } from "../../../../../components/componentOverride/componentOverrideContext";
19+
import FeatureWrapper from "../../../../../components/featureWrapper";
20+
import SuperTokens from "../../../../../superTokens";
1721
import { getQueryParams } from "../../../../../utils";
22+
import { defaultTranslationsEmailPassword } from "../../../../emailpassword/components/themes/translations";
23+
import RecoverAccountWithToken from "../../themes/recoverAccountWithToken";
24+
25+
import type { RecoverAccountWithTokenProps } from "../../../types";
1826

19-
export const RecoverAccountUsingToken = (props): JSX.Element => {
27+
export const RecoverAccountUsingToken: React.FC<RecoverAccountWithTokenProps> = (props): JSX.Element => {
2028
const token = getQueryParams("token");
21-
console.log("token: ", token);
29+
let userContext;
30+
if (props.userContext !== undefined) {
31+
userContext = props.userContext;
32+
}
33+
const [error, setError] = React.useState<string>();
34+
35+
const childProps = {
36+
config: props.recipe.config,
37+
error: error,
38+
onError: (error: string) => setError(error),
39+
clearError: () => setError(undefined),
40+
recipeImplementation: props.recipe.webJSRecipe,
41+
token,
42+
useComponentOverride: props.useComponentOverrides,
43+
userContext,
44+
};
45+
const recipeComponentOverrides = props.useComponentOverrides();
46+
47+
return (
48+
<ComponentOverrideContext.Provider value={recipeComponentOverrides}>
49+
<FeatureWrapper
50+
useShadowDom={SuperTokens.getInstanceOrThrow().useShadowDom}
51+
defaultStore={defaultTranslationsEmailPassword}>
52+
<React.Fragment>
53+
{/* No custom theme, use default. */}
54+
{props.children === undefined && <RecoverAccountWithToken {...childProps} />}
55+
{/* Otherwise, custom theme is provided, propagate props. */}
56+
{props.children &&
57+
React.Children.map(props.children, (child) => {
58+
if (React.isValidElement(child)) {
59+
return React.cloneElement(child, childProps);
60+
}
2261

23-
return <div>Recovering using token</div>;
62+
return child;
63+
})}
64+
</React.Fragment>
65+
</FeatureWrapper>
66+
</ComponentOverrideContext.Provider>
67+
);
2468
};
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/* Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved.
2+
*
3+
* This software is licensed under the Apache License, Version 2.0 (the
4+
* "License") as published by the Apache Software Foundation.
5+
*
6+
* You may not use this file except in compliance with the License. You may
7+
* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
* License for the specific language governing permissions and limitations
13+
* under the License.
14+
*/
15+
16+
import { useState } from "react";
17+
18+
import { SuperTokensBranding } from "../../../../../components/SuperTokensBranding";
19+
import SuperTokens from "../../../../../superTokens";
20+
import { AuthPageFooter, AuthPageHeader } from "../../../../../ui";
21+
import UserContextWrapper from "../../../../../usercontext/userContextWrapper";
22+
import { PasskeyConfirmation } from "../signUp/confirmation";
23+
import { ThemeBase } from "../themeBase";
24+
25+
import type { RecoverAccountWithTokenThemeProps } from "../../../types";
26+
27+
export enum RecoverAccountScreen {
28+
ContinueWithPasskey,
29+
Success,
30+
}
31+
32+
function PasskeyRecoverAccountWithTokenTheme(props: RecoverAccountWithTokenThemeProps): JSX.Element {
33+
const rootStyle = SuperTokens.getInstanceOrThrow().rootStyle;
34+
35+
const activeStyle = props.config.signUpFeature.style;
36+
const stInstance = SuperTokens.getInstanceOrThrow();
37+
38+
const privacyPolicyLink = stInstance.privacyPolicyLink;
39+
const termsOfServiceLink = stInstance.termsOfServiceLink;
40+
41+
const [activeScreen] = useState<RecoverAccountScreen>(RecoverAccountScreen.ContinueWithPasskey);
42+
43+
const onResetFactorList = () => {
44+
throw new Error("Should never come here as we don't have back functionality");
45+
};
46+
47+
return (
48+
<UserContextWrapper userContext={props.userContext}>
49+
<ThemeBase userStyles={[rootStyle, props.config.recipeRootStyle, activeStyle]}>
50+
<div data-supertokens="container authPage singleFactor">
51+
<div data-supertokens="row">
52+
<AuthPageHeader
53+
factorIds={["webauthn"]}
54+
isSignUp={true}
55+
onSignInUpSwitcherClick={undefined}
56+
hasSeparateSignUpView={true}
57+
resetFactorList={onResetFactorList}
58+
showBackButton={false}
59+
oauth2ClientInfo={undefined}
60+
headerLabel={
61+
activeScreen === RecoverAccountScreen.ContinueWithPasskey
62+
? "WEBAUTHN_CREATE_A_PASSKEY_HEADER"
63+
: undefined
64+
}
65+
hideSignInSwitcher={true}
66+
/>
67+
<RecoverAccountThemeInner {...props} activeScreen={activeScreen} />
68+
{activeScreen !== RecoverAccountScreen.Success && (
69+
<AuthPageFooter
70+
factorIds={[]}
71+
isSignUp={true}
72+
hasSeparateSignUpView={true}
73+
privacyPolicyLink={privacyPolicyLink}
74+
termsOfServiceLink={termsOfServiceLink}
75+
/>
76+
)}
77+
</div>
78+
<SuperTokensBranding />
79+
</div>
80+
</ThemeBase>
81+
</UserContextWrapper>
82+
);
83+
}
84+
85+
const RecoverAccountThemeInner = (
86+
props: RecoverAccountWithTokenThemeProps & {
87+
activeScreen: RecoverAccountScreen;
88+
}
89+
) => {
90+
return props.activeScreen === RecoverAccountScreen.ContinueWithPasskey ? (
91+
<PasskeyConfirmation
92+
{...props}
93+
email={""}
94+
onContinueClick={() => {}}
95+
// errorMessageLabel={showPasskeyConfirmationError ? "WEBAUTHN_PASSKEY_RECOVERABLE_ERROR" : undefined}
96+
isLoading={false}
97+
onFetchError={() => {}}
98+
resetFactorList={() => {}}
99+
/>
100+
) : props.activeScreen === RecoverAccountScreen.Success ? (
101+
<div></div>
102+
) : null;
103+
};
104+
105+
export default PasskeyRecoverAccountWithTokenTheme;

lib/ts/recipe/webauthn/prebuiltui.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,17 @@ export class WebauthnPreBuiltUI extends RecipeRouter {
7979
getFeatureComponent = (
8080
componentName: "webauthn-recover-account",
8181
props: FeatureBaseProps<{ userContext?: UserContext }>,
82-
_: () => GenericComponentOverrideMap<any> = useRecipeComponentOverrideContext
82+
useComponentOverrides: () => GenericComponentOverrideMap<any> = useRecipeComponentOverrideContext
8383
): JSX.Element => {
8484
if (componentName === "webauthn-recover-account") {
8585
return (
8686
<UserContextWrapper userContext={props.userContext}>
8787
<SessionAuth requireAuth={false} doRedirection={false}>
88-
<RecoverAccountUsingToken />
88+
<RecoverAccountUsingToken
89+
recipe={this.recipeInstance}
90+
{...props}
91+
useComponentOverrides={useComponentOverrides}
92+
/>
8993
</SessionAuth>
9094
</UserContextWrapper>
9195
);

lib/ts/recipe/webauthn/recipe.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import WebauthnWebJS from "supertokens-web-js/lib/build/recipe/webauthn";
1717

1818
import { SSR_ERROR } from "../../constants";
1919
import AuthRecipe from "../authRecipe";
20+
import { FactorIds } from "../multifactorauth";
2021

2122
import { getFunctionOverrides } from "./functionOverrides";
2223
import { normaliseWebauthnConfig } from "./utils";
@@ -35,7 +36,6 @@ import type {
3536
RecipeInitResult,
3637
} from "../../types";
3738
import type RecipeModule from "../recipeModule";
38-
import { FactorIds } from "../multifactorauth";
3939

4040
export default class Webauthn extends AuthRecipe<
4141
GetRedirectionURLContext,

lib/ts/recipe/webauthn/types.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
* under the License.
1414
*/
1515

16+
import type Recipe from "./recipe";
17+
import type { ComponentOverride } from "../../components/componentOverride/componentOverride";
1618
import type {
1719
FeatureBaseConfig,
1820
NormalisedBaseConfig,
@@ -27,6 +29,7 @@ import type {
2729
NormalisedConfig as NormalisedAuthRecipeModuleConfig,
2830
Config as AuthRecipeModuleConfig,
2931
} from "../authRecipe/types";
32+
import type SignInForm from "./components/features/signIn";
3033
import type { InputProps } from "../emailpassword/components/library/input";
3134
import type WebJSRecipe from "supertokens-web-js/recipe/webauthn";
3235
import type { RecipeInterface } from "supertokens-web-js/recipe/webauthn";
@@ -131,7 +134,9 @@ export type NormalisedConfig = {
131134

132135
export type RecipeImplementation = WebJSRecipeInterface<typeof WebJSRecipe>;
133136

134-
export type ComponentOverrideMap = Record<string, undefined>;
137+
export type ComponentOverrideMap = {
138+
PasskeySignInForm_Override?: ComponentOverride<typeof SignInForm>;
139+
};
135140

136141
export type WebauthnSignUpState = {
137142
showBackButton: boolean;
@@ -166,6 +171,21 @@ export type SignUpFormProps = {
166171
resetFactorList: () => void;
167172
};
168173

174+
export type RecoverAccountWithTokenProps = {
175+
userContext?: UserContext | undefined;
176+
recipe: Recipe;
177+
useComponentOverrides: () => ComponentOverrideMap;
178+
};
179+
180+
export type RecoverAccountWithTokenThemeProps = {
181+
config: NormalisedConfig;
182+
userContext?: UserContext;
183+
recipeImplementation: RecipeImplementation;
184+
error: string | undefined;
185+
clearError: () => void;
186+
onError: (error: string) => void;
187+
};
188+
169189
export type ContinueOnSuccessParams = {
170190
email: string;
171191
};

0 commit comments

Comments
 (0)