1
1
import * as React from "react" ;
2
- import STGeneralError from "supertokens-web-js/utils/error" ;
3
- import { Fragment } from "react" ;
4
- import { defaultEmailValidator } from "../../../../emailpassword/validators" ;
5
- import Button from "../../../../emailpassword/components/library/button" ;
6
- import SpinnerIcon from "../../../../../components/assets/spinnerIcon" ;
7
- import PasskeyIcon from "../../../../../components/assets/passkeyIcon" ;
8
2
import { WebAuthnMFAProps } from "../../../types" ;
9
3
import SuperTokens from "../../../../../superTokens" ;
10
- import FormBase from "../../../../emailpassword/components/library/formBase" ;
11
- import { Label } from "../../../../emailpassword/components/library" ;
12
- import { useTranslation } from "../../../../../translation/translationContext" ;
13
4
import UserContextWrapper from "../../../../../usercontext/userContextWrapper" ;
14
- import GeneralError from "../../../../emailpassword/components/library/generalError" ;
5
+ import { useTranslation } from "../../../../../translation/translationContext" ;
6
+ import { AccessDeniedScreen } from "../../../../session/prebuiltui" ;
15
7
import { ThemeBase } from "../themeBase" ;
16
- import BackButton from "../../../../emailpassword/components/library/backButton" ;
17
8
import { SuperTokensBranding } from "../../../../../components/SuperTokensBranding" ;
18
- import { withOverride } from "../../../../../components/componentOverride/withOverride" ;
19
- import { PasskeyFeatureBlockList } from "../signUp/featureBlocks" ;
20
- import { PasskeyNotSupportedError } from "../error/passkeyNotSupportedError" ;
9
+ import { WebauthnMFALoadingScreen } from "./loadingScreen" ;
10
+ import { WebauthnMFASignIn } from "./signIn" ;
11
+ import { WebauthnMFASignUp } from "./signUp" ;
12
+ import { WebauthnMFASignUpConfirmation } from "./signUpConfirmation" ;
13
+
14
+ export { WebauthnMFALoadingScreen , WebauthnMFASignIn , WebauthnMFASignUp , WebauthnMFASignUpConfirmation } ;
15
+ export type { MFASignInProps } from "./signIn" ;
16
+ export type { MFASignUpProps } from "./signUp" ;
17
+ export type { MFASignUpConfirmationProps } from "./signUpConfirmation" ;
21
18
22
19
export enum MFAScreens {
23
20
SignIn ,
@@ -43,6 +40,7 @@ export function MFATheme(props: WebAuthnMFAProps): JSX.Element {
43
40
const { onBackButtonClicked, onSignIn } = props ;
44
41
const [ activeScreen , setActiveScreen ] = React . useState < MFAScreens > ( MFAScreens . SignIn ) ;
45
42
const [ signUpEmail , setSignUpEmail ] = React . useState < string > ( "" ) ;
43
+ const t = useTranslation ( ) ;
46
44
47
45
const onRegisterPasskeyClick = React . useCallback ( ( ) => {
48
46
if ( props . featureState . email ) {
@@ -88,6 +86,15 @@ export function MFATheme(props: WebAuthnMFAProps): JSX.Element {
88
86
return < WebauthnMFALoadingScreen /> ;
89
87
}
90
88
89
+ if ( props . featureState . accessDenied ) {
90
+ return (
91
+ < AccessDeniedScreen
92
+ useShadowDom = { false /* We set this to false, because we are already inside a shadowDom (if required) */ }
93
+ error = { t ( props . featureState . error ! ) }
94
+ />
95
+ ) ;
96
+ }
97
+
91
98
return (
92
99
< div data-supertokens = "container webauthn-mfa" >
93
100
< div data-supertokens = "row" >
@@ -123,220 +130,3 @@ export function MFATheme(props: WebAuthnMFAProps): JSX.Element {
123
130
</ div >
124
131
) ;
125
132
}
126
-
127
- export const WebauthnMFALoadingScreen = withOverride ( "WebauthnMFALoadingScreen" , function WebauthnMFALoadingScreen ( ) {
128
- return (
129
- < div data-supertokens = "container delayedRender pwless-mfa loadingScreen" >
130
- < div data-supertokens = "row" >
131
- < div data-supertokens = "spinner delayedRender" >
132
- < SpinnerIcon />
133
- </ div >
134
- </ div >
135
- </ div >
136
- ) ;
137
- } ) ;
138
-
139
- type MFASignInProps = {
140
- onBackButtonClicked ?: ( ) => void ;
141
- onSignIn : ( ) => Promise < void > ;
142
- onRegisterPasskeyClick : ( ) => void ;
143
- error : string | undefined ;
144
- deviceSupported : boolean ;
145
- } ;
146
-
147
- export const WebauthnMFASignIn = withOverride (
148
- "WebauthnMFASignIn" ,
149
- function WebauthnMFASignIn ( props : MFASignInProps ) : JSX . Element {
150
- const t = useTranslation ( ) ;
151
- const [ isLoading , setIsLoading ] = React . useState ( false ) ;
152
-
153
- const onClick = React . useCallback ( async ( ) => {
154
- setIsLoading ( true ) ;
155
- await props . onSignIn ( ) ;
156
- setIsLoading ( false ) ;
157
- } , [ props ] ) ;
158
-
159
- return (
160
- < Fragment >
161
- { props . onBackButtonClicked ? (
162
- < div data-supertokens = "headerTitle withBackButton webauthn-mfa" >
163
- < BackButton onClick = { props . onBackButtonClicked } />
164
- { t ( "WEBAUTHN_MFA_SIGN_IN_HEADER_TITLE" ) }
165
- < span data-supertokens = "backButtonPlaceholder backButtonCommon" >
166
- { /* empty span for spacing the back button */ }
167
- </ span >
168
- </ div >
169
- ) : (
170
- < div data-supertokens = "headerTitle ebauthn-mfa" > { t ( "WEBAUTHN_MFA_SIGN_IN_HEADER_TITLE" ) } </ div >
171
- ) }
172
- < div data-supertokens = "headerSubtitle secondaryText" > { t ( "WEBAUTHN_MFA_SIGN_IN_HEADER_SUBTITLE" ) } </ div >
173
- < Button
174
- disabled = { ! props . deviceSupported || isLoading }
175
- isLoading = { isLoading }
176
- type = "button"
177
- onClick = { onClick }
178
- label = "WEBAUTHN_EMAIL_CONTINUE_BUTTON"
179
- isGreyedOut = { ! props . deviceSupported }
180
- icon = { PasskeyIcon }
181
- />
182
- { props . error !== undefined && < GeneralError error = { props . error } /> }
183
- < div data-supertokens = "passkeyMfaSignInDivider" >
184
- < div data-supertokens = "divider" />
185
- < span > or</ span >
186
- < div data-supertokens = "divider" />
187
- </ div >
188
- < div data-supertokens = "headerSubtitle secondaryText" >
189
- < span data-supertokens = "link" onClick = { props . onRegisterPasskeyClick } >
190
- { t ( "WEBAUTHN_MFA_REGISTER_PASSKEY_LINK" ) }
191
- </ span >
192
- { t ( "WEBAUTHN_MFA_REGISTER_PASSKEY_SUBTITLE" ) }
193
- </ div >
194
- { ! props . deviceSupported && < PasskeyNotSupportedError /> }
195
- </ Fragment >
196
- ) ;
197
- }
198
- ) ;
199
-
200
- type MFASignUpProps = {
201
- onContinueClick : ( email : string ) => void ;
202
- clearError : ( ) => void ;
203
- email ?: string ;
204
- error ?: string ;
205
- onError : ( error : string ) => void ;
206
- onFetchError ?: ( error : Response ) => void ;
207
- onRecoverAccountClick : ( ) => void ;
208
- onBackButtonClicked : ( ) => void ;
209
- } ;
210
-
211
- export const WebauthnMFASignUp = withOverride (
212
- "WebauthnMFASignUp" ,
213
- function WebauthnMFASignUp ( props : MFASignUpProps ) : JSX . Element {
214
- const t = useTranslation ( ) ;
215
-
216
- const onSuccess = React . useCallback (
217
- ( { email } : { email : string } ) => {
218
- props . onContinueClick ( email ) ;
219
- } ,
220
- [ props . onContinueClick ]
221
- ) ;
222
-
223
- return (
224
- < Fragment >
225
- < div data-supertokens = "headerTitle withBackButton webauthn-mfa" >
226
- < BackButton onClick = { props . onBackButtonClicked } />
227
- { t ( "WEBAUTHN_MFA_REGISTER_PASSKEY_LINK" ) }
228
- < span data-supertokens = "backButtonPlaceholder backButtonCommon" >
229
- { /* empty span for spacing the back button */ }
230
- </ span >
231
- </ div >
232
- { props . error !== undefined && < GeneralError error = { props . error } /> }
233
- < div data-supertokens = "signUpFormInnerContainer" >
234
- < div data-supertokens = "cautionMessage" > { t ( "WEBAUTHN_SIGN_UP_CAUTION_MESSAGE_LABEL" ) } </ div >
235
- < FormBase
236
- clearError = { props . clearError }
237
- onFetchError = { props . onFetchError }
238
- onError = { props . onError }
239
- formFields = { [
240
- {
241
- id : "email" ,
242
- label : "" ,
243
- labelComponent : (
244
- < div data-supertokens = "formLabelWithLinkWrapper" >
245
- < Label value = { "WEBAUTHN_SIGN_UP_LABEL" } data-supertokens = "emailInputLabel" />
246
- < a
247
- onClick = { props . onRecoverAccountClick }
248
- data-supertokens = "link linkButton formLabelLinkBtn recoverAccountTrigger" >
249
- { t ( "WEBAUTHN_RECOVER_ACCOUNT_LABEL" ) }
250
- </ a >
251
- </ div >
252
- ) ,
253
- optional : false ,
254
- autofocus : true ,
255
- placeholder : "" ,
256
- getDefaultValue : ( ) => props . email as string ,
257
- autoComplete : "email" ,
258
- // We are using the default validator that allows any string
259
- validate : defaultEmailValidator ,
260
- } ,
261
- ] }
262
- buttonLabel = { "WEBAUTHN_EMAIL_CONTINUE_BUTTON" }
263
- onSuccess = { onSuccess }
264
- callAPI = { async ( formFields ) => {
265
- const email = formFields . find ( ( field ) => field . id === "email" ) ?. value ;
266
- if ( email === undefined ) {
267
- throw new STGeneralError ( "GENERAL_ERROR_EMAIL_UNDEFINED" ) ;
268
- }
269
-
270
- if ( email === "" ) {
271
- throw new STGeneralError ( "EMAIL_INPUT_NOT_POPULATED_ERROR" ) ;
272
- }
273
-
274
- // We do not want the form to make the API call since we have
275
- // an intermediary step here so we will just mock an OK status
276
- // to render the next step.
277
- return {
278
- status : "OK" ,
279
- email,
280
- } ;
281
- } }
282
- validateOnBlur = { false }
283
- showLabels = { true }
284
- footer = { undefined }
285
- />
286
- </ div >
287
- </ Fragment >
288
- ) ;
289
- }
290
- ) ;
291
-
292
- type MFASignUpConfirmationProps = {
293
- onSignUp : ( email : string ) => Promise < void > ;
294
- onBackButtonClicked : ( ) => void ;
295
- email : string ;
296
- error ?: string ;
297
- } ;
298
-
299
- export const WebauthnMFASignUpConfirmation = withOverride (
300
- "WebauthnMFASignUpConfirmation" ,
301
- function WebauthnMFASignUpConfirmation ( props : MFASignUpConfirmationProps ) : JSX . Element {
302
- const t = useTranslation ( ) ;
303
- const [ isLoading , setIsLoading ] = React . useState ( false ) ;
304
-
305
- const onClick = React . useCallback ( async ( ) => {
306
- setIsLoading ( true ) ;
307
- await props . onSignUp ( props . email ) ;
308
- setIsLoading ( false ) ;
309
- } , [ props ] ) ;
310
-
311
- return (
312
- < Fragment >
313
- < div data-supertokens = "headerTitle withBackButton webauthn-mfa" >
314
- < BackButton onClick = { props . onBackButtonClicked } />
315
- { t ( "WEBAUTHN_MFA_REGISTER_PASSKEY_LINK" ) }
316
- < span data-supertokens = "backButtonPlaceholder backButtonCommon" >
317
- { /* empty span for spacing the back button */ }
318
- </ span >
319
- </ div >
320
- < div data-supertokens = "divider" />
321
- < div data-supertokens = "passkeyConfirmationContainer" >
322
- < div data-supertokens = "passkeyConfirmationEmailContainer" >
323
- < div data-supertokens = "continueWithLabel" > { t ( "WEBAUTHN_CONTINUE_WITH_EMAIL_SUBTEXT" ) } </ div >
324
- < div data-supertokens = "enteredEmailId" > { props . email } </ div >
325
- </ div >
326
- < PasskeyFeatureBlockList />
327
- { props . error !== undefined && < GeneralError error = { props . error } /> }
328
- < div data-supertokens = "passkeyConfirmationFooter" >
329
- < Button
330
- disabled = { isLoading }
331
- isLoading = { isLoading }
332
- type = "button"
333
- onClick = { onClick }
334
- label = "WEBAUTHN_EMAIL_CONTINUE_BUTTON"
335
- isGreyedOut = { false }
336
- />
337
- </ div >
338
- </ div >
339
- </ Fragment >
340
- ) ;
341
- }
342
- ) ;
0 commit comments