11import * 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" ;
82import { WebAuthnMFAProps } from "../../../types" ;
93import SuperTokens from "../../../../../superTokens" ;
10- import FormBase from "../../../../emailpassword/components/library/formBase" ;
11- import { Label } from "../../../../emailpassword/components/library" ;
12- import { useTranslation } from "../../../../../translation/translationContext" ;
134import UserContextWrapper from "../../../../../usercontext/userContextWrapper" ;
14- import GeneralError from "../../../../emailpassword/components/library/generalError" ;
5+ import { useTranslation } from "../../../../../translation/translationContext" ;
6+ import { AccessDeniedScreen } from "../../../../session/prebuiltui" ;
157import { ThemeBase } from "../themeBase" ;
16- import BackButton from "../../../../emailpassword/components/library/backButton" ;
178import { 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" ;
2118
2219export enum MFAScreens {
2320 SignIn ,
@@ -43,6 +40,7 @@ export function MFATheme(props: WebAuthnMFAProps): JSX.Element {
4340 const { onBackButtonClicked, onSignIn } = props ;
4441 const [ activeScreen , setActiveScreen ] = React . useState < MFAScreens > ( MFAScreens . SignIn ) ;
4542 const [ signUpEmail , setSignUpEmail ] = React . useState < string > ( "" ) ;
43+ const t = useTranslation ( ) ;
4644
4745 const onRegisterPasskeyClick = React . useCallback ( ( ) => {
4846 if ( props . featureState . email ) {
@@ -88,6 +86,15 @@ export function MFATheme(props: WebAuthnMFAProps): JSX.Element {
8886 return < WebauthnMFALoadingScreen /> ;
8987 }
9088
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+
9198 return (
9299 < div data-supertokens = "container webauthn-mfa" >
93100 < div data-supertokens = "row" >
@@ -123,220 +130,3 @@ export function MFATheme(props: WebAuthnMFAProps): JSX.Element {
123130 </ div >
124131 ) ;
125132}
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