|
18 | 18 | import * as React from "react"; |
19 | 19 |
|
20 | 20 | import AuthComponentWrapper from "../../../../../components/authCompWrapper"; |
21 | | -import FeatureWrapper from "../../../../../components/featureWrapper"; |
22 | | -import SuperTokens from "../../../../../superTokens"; |
| 21 | +import { useUserContext } from "../../../../../usercontext"; |
| 22 | +import { useRethrowInRender } from "../../../../../utils"; |
| 23 | +import { handleFormSubmit } from "../../../../emailpassword/components/library/functions/form"; |
| 24 | +import { useSessionContext } from "../../../../session"; |
| 25 | +import Session from "../../../../session/recipe"; |
23 | 26 | import { ContinueWithPasskeyTheme } from "../../themes/continueWithPasskey"; |
24 | | -import { defaultTranslationsWebauthn } from "../../themes/translations"; |
25 | 27 |
|
26 | | -import type { UserContext, PartialAuthComponentProps } from "../../../../../types"; |
| 28 | +import type { UserContext, PartialAuthComponentProps, APIFormField } from "../../../../../types"; |
| 29 | +import type { AuthSuccessContext } from "../../../../authRecipe/types"; |
27 | 30 | import type Recipe from "../../../recipe"; |
28 | | -import type { ComponentOverrideMap } from "../../../types"; |
| 31 | +import type { ComponentOverrideMap, SignInThemeProps } from "../../../types"; |
| 32 | +import type { User } from "supertokens-web-js/types"; |
29 | 33 |
|
30 | | -export const SignInWithPasskeyFeature: React.FC< |
| 34 | +export function useChildProps( |
| 35 | + recipe: Recipe, |
| 36 | + factorIds: string[], |
| 37 | + onAuthSuccess: (successContext: AuthSuccessContext) => Promise<void>, |
| 38 | + error: string | undefined, |
| 39 | + onError: (err: string) => void, |
| 40 | + userContext: UserContext, |
| 41 | + clearError: () => void, |
| 42 | + resetFactorList: () => void, |
| 43 | + onSignInUpSwitcherClick: () => void |
| 44 | +): SignInThemeProps { |
| 45 | + const session = useSessionContext(); |
| 46 | + const recipeImplementation = recipe.webJSRecipe; |
| 47 | + const rethrowInRender = useRethrowInRender(); |
| 48 | + |
| 49 | + return React.useMemo(() => { |
| 50 | + return { |
| 51 | + userContext, |
| 52 | + onSuccess: async (result: { createdNewRecipeUser: boolean; user: User }) => { |
| 53 | + let payloadAfterCall; |
| 54 | + try { |
| 55 | + payloadAfterCall = await Session.getInstanceOrThrow().getAccessTokenPayloadSecurely({ |
| 56 | + userContext, |
| 57 | + }); |
| 58 | + } catch { |
| 59 | + payloadAfterCall = undefined; |
| 60 | + } |
| 61 | + return onAuthSuccess({ |
| 62 | + createdNewUser: result.createdNewRecipeUser && result.user.loginMethods.length === 1, |
| 63 | + isNewRecipeUser: result.createdNewRecipeUser, |
| 64 | + newSessionCreated: |
| 65 | + session.loading || |
| 66 | + !session.doesSessionExist || |
| 67 | + (payloadAfterCall !== undefined && |
| 68 | + session.accessTokenPayload.sessionHandle !== payloadAfterCall.sessionHandle), |
| 69 | + recipeId: "webauthn", |
| 70 | + }).catch(rethrowInRender); |
| 71 | + }, |
| 72 | + error, |
| 73 | + onError, |
| 74 | + clearError, |
| 75 | + onFetchError: async (/* err: Response*/) => { |
| 76 | + // TODO: Do we need to do something else? |
| 77 | + onError("SOMETHING_WENT_WRONG_ERROR"); |
| 78 | + }, |
| 79 | + factorIds, |
| 80 | + recipeImplementation: recipeImplementation, |
| 81 | + config: recipe.config, |
| 82 | + resetFactorList: resetFactorList, |
| 83 | + onSignInUpSwitcherClick, |
| 84 | + }; |
| 85 | + }, [error, factorIds, userContext, recipeImplementation]); |
| 86 | +} |
| 87 | + |
| 88 | +const SignInFeatureInner: React.FC< |
31 | 89 | PartialAuthComponentProps & { |
32 | 90 | recipe: Recipe; |
33 | 91 | factorIds: string[]; |
34 | | - userContext?: UserContext; |
35 | 92 | useComponentOverrides: () => ComponentOverrideMap; |
| 93 | + userContext?: UserContext; |
36 | 94 | } |
37 | 95 | > = (props) => { |
38 | | - const recipeComponentOverrides = props.useComponentOverrides(); |
| 96 | + let userContext = useUserContext(); |
| 97 | + if (props.userContext !== undefined) { |
| 98 | + userContext = props.userContext; |
| 99 | + } |
| 100 | + const childProps = useChildProps( |
| 101 | + props.recipe, |
| 102 | + props.factorIds, |
| 103 | + props.onAuthSuccess, |
| 104 | + props.error, |
| 105 | + props.onError, |
| 106 | + userContext, |
| 107 | + props.clearError, |
| 108 | + props.resetFactorList, |
| 109 | + props.onSignInUpSwitcherClick |
| 110 | + )!; |
39 | 111 |
|
40 | | - // TODO: Define the code to handle sign in properly through this component. |
41 | | - const handleWebauthnSignInClick = () => { |
42 | | - alert("This is yet to be defined!"); |
43 | | - return; |
| 112 | + const callAPI = React.useCallback( |
| 113 | + async (_: APIFormField[], __: (id: string, value: string) => any) => { |
| 114 | + const email = prompt("Enter email ID"); |
| 115 | + if (email === null) { |
| 116 | + alert("Please enter an email"); |
| 117 | + return; |
| 118 | + } |
| 119 | + |
| 120 | + const response = await childProps.recipeImplementation.authenticateCredentialWithSignIn({ |
| 121 | + email: email, |
| 122 | + userContext, |
| 123 | + }); |
| 124 | + |
| 125 | + return response; |
| 126 | + }, |
| 127 | + [childProps, userContext] |
| 128 | + ); |
| 129 | + |
| 130 | + // Define the code to handle sign in properly through this component. |
| 131 | + const handleWebauthnSignInClick = async () => { |
| 132 | + await handleFormSubmit({ |
| 133 | + callAPI: callAPI, |
| 134 | + clearError: () => alert("Clear error"), |
| 135 | + onError: () => alert("Error"), |
| 136 | + onFetchError: () => alert("Fetch error"), |
| 137 | + onSuccess: (payload) => console.warn("payload: ", payload), |
| 138 | + }); |
44 | 139 | }; |
45 | 140 |
|
46 | 141 | return ( |
47 | | - <AuthComponentWrapper recipeComponentOverrides={recipeComponentOverrides}> |
48 | | - <FeatureWrapper |
49 | | - useShadowDom={SuperTokens.getInstanceOrThrow().useShadowDom} |
50 | | - defaultStore={defaultTranslationsWebauthn}> |
| 142 | + <React.Fragment> |
| 143 | + {/* No custom theme, use default. */} |
| 144 | + {props.children === undefined && ( |
51 | 145 | <ContinueWithPasskeyTheme |
52 | 146 | {...props} |
53 | 147 | continueWithPasskeyClicked={handleWebauthnSignInClick} |
54 | 148 | config={props.recipe.config} |
55 | 149 | continueFor="SIGN_IN" |
56 | 150 | /> |
57 | | - </FeatureWrapper> |
| 151 | + )} |
| 152 | + |
| 153 | + {/* Otherwise, custom theme is provided, propagate props. */} |
| 154 | + {props.children && |
| 155 | + React.Children.map(props.children, (child) => { |
| 156 | + if (React.isValidElement(child)) { |
| 157 | + return React.cloneElement(child, { |
| 158 | + ...childProps, |
| 159 | + }); |
| 160 | + } |
| 161 | + return child; |
| 162 | + })} |
| 163 | + </React.Fragment> |
| 164 | + ); |
| 165 | +}; |
| 166 | + |
| 167 | +export const SignInWithPasskeyFeature: React.FC< |
| 168 | + PartialAuthComponentProps & { |
| 169 | + recipe: Recipe; |
| 170 | + factorIds: string[]; |
| 171 | + userContext?: UserContext; |
| 172 | + useComponentOverrides: () => ComponentOverrideMap; |
| 173 | + } |
| 174 | +> = (props) => { |
| 175 | + const recipeComponentOverrides = props.useComponentOverrides(); |
| 176 | + |
| 177 | + return ( |
| 178 | + <AuthComponentWrapper recipeComponentOverrides={recipeComponentOverrides}> |
| 179 | + <SignInFeatureInner {...props} /> |
58 | 180 | </AuthComponentWrapper> |
59 | 181 | ); |
60 | 182 | }; |
|
0 commit comments