1313 * under the License.
1414 */
1515
16- import { useCallback , useState } from "react" ;
16+ import { useCallback , useEffect , useState } from "react" ;
1717
18+ import { redirectToAuth } from "../../../../.." ;
1819import { SuperTokensBranding } from "../../../../../components/SuperTokensBranding" ;
1920import SuperTokens from "../../../../../superTokens" ;
2021import { AuthPageFooter , AuthPageHeader } from "../../../../../ui" ;
2122import UserContextWrapper from "../../../../../usercontext/userContextWrapper" ;
2223import { PasskeyConfirmation } from "../signUp/confirmation" ;
2324import { ThemeBase } from "../themeBase" ;
2425
25- import type { RecoverAccountWithTokenThemeProps } from "../../../types" ;
2626import { PasskeyRecoverAccountSuccess } from "./success" ;
2727
28+ import type { RecoverAccountWithTokenThemeProps } from "../../../types" ;
29+
2830export 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 ? (
0 commit comments