No invitation token found.
- } + const handleSubmit = async (formData: FormData) => { + const firstName = formData.get('firstName') as string + const lastName = formData.get('lastName') as string + const password = formData.get('password') as string - // Handle submission of the sign-up form - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault() - - if (!isLoaded) return - - try { - if (!token) return null - - // Create a new sign-up with the supplied invitation token. - // Make sure you're also passing the ticket strategy. - // After the below call, the user's email address will be - // automatically verified because of the invitation token. - const signUpAttempt = await signUp.create({ - strategy: 'ticket', - ticket: token, - firstName, - lastName, - password, + await signUp.ticket({ + firstName, + lastName, + password, + }) + if (signUp.status === 'complete') { + await signUp.finalize({ + navigate: () => { + router.push('/') + }, }) - - // If the sign-up was completed, set the session to active - if (signUpAttempt.status === 'complete') { - await setActive({ session: signUpAttempt.createdSessionId }) - } else { - // If the sign-up status is not complete, check why. User may need to - // complete further steps. - console.error(JSON.stringify(signUpAttempt, null, 2)) - } - } catch (err) { - console.error(JSON.stringify(err, null, 2)) } } + if (signUp.status === 'complete' || isSignedIn) { + return null + } + return ( <>No invitation token found.
+ } + + // Handle submission of the sign-up form + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + + if (!isLoaded) return + + try { + if (!token) return null + + // Create a new sign-up with the supplied invitation token. + // Make sure you're also passing the ticket strategy. + // After the below call, the user's email address will be + // automatically verified because of the invitation token. + const signUpAttempt = await signUp.create({ + strategy: 'ticket', + ticket: token, + firstName, + lastName, + password, + }) + + // If the sign-up was completed, set the session to active + if (signUpAttempt.status === 'complete') { + await setActive({ session: signUpAttempt.createdSessionId }) + } else { + // If the sign-up status is not complete, check why. User may need to + // complete further steps. + console.error(JSON.stringify(signUpAttempt, null, 2)) + } + } catch (err) { + console.error(JSON.stringify(err, null, 2)) + } + } + + return ( + <> +Error: {error}
+ +Check your email and visit the link that was sent to you.
+ +The email link verification failed.
+ Sign up +The email link has expired.
+ Sign up ++ You must complete the email link sign-up on the same device and browser that you started + it on. +
+ Sign up +Successfully signed up. Return to the original tab to continue.
+Error: {error}
+ +Check your email and visit the link that was sent to you.
+ +The email link verification failed.
+ Sign in +The email link has expired.
+ Sign in ++ You must complete the email link sign-in on the same device and browser as you started it + on. +
+ Sign in +Successfully signed in. Return to the original tab to continue.
+Passkeys:
+Passkey updated: {success ? 'Yes' : 'No'}
+ > + ) +} +``` + +## Delete user passkeys + +To delete a user's passkey from your Clerk app, you must call the [`delete()`](/docs/reference/javascript/types/passkey-resource#delete) method of the passkey object, as shown in the following example: + +```tsx {{ filename: 'components/DeletePasskeyUI.tsx' }} +export function DeletePasskeyUI() { + const { user } = useUser() + const { passkeys } = user + + const passkeyToDeleteId = useRefPasskeys:
+Passkey deleted: {success ? 'Yes' : 'No'}
+ > + ) +} +``` diff --git a/docs/guides/development/custom-flows/authentication/sign-out.mdx b/docs/guides/development/custom-flows/authentication/sign-out.mdx index 01ee0d2ba1..4f0843eea5 100644 --- a/docs/guides/development/custom-flows/authentication/sign-out.mdx +++ b/docs/guides/development/custom-flows/authentication/sign-out.mdx @@ -1,6 +1,7 @@ --- title: Build a custom sign-out flow description: Learn how to use the Clerk API to build a custom sign-out flow using Clerk's signOut() function. +sdk: nextjs, react, expo, js-frontend, react-router, tanstack-react-start, ios, android ---[SignInFirstFactor](/docs/reference/javascript/types/sign-in-first-factor)\[]
+
+ The list of first-factor strategies that are available for the current sign-in attempt.
+
+ ---
+
+ - `supportedSecondFactors`
+ - [SignInSecondFactor](/docs/reference/javascript/types/sign-in-second-factor)\[]
+
+ The list of second-factor strategies that are available for the current sign-in attempt.
+
+ ---
+
+ - `status`
+ - `SignInStatus`
+
+ The current status of the sign-in. `SignInStatus` supports the following values:
+
+ - `'complete'`: The user is signed in and the custom flow can proceed to `signIn.finalize()` to create a session.
+ - `'needs_identifier'`: The user's identifier (e.g., email address, phone number, username) hasn't been provided.
+ - `'needs_first_factor'`: One of the following [first factor verification strategies](/docs/reference/javascript/sign-in) is missing: `'email_link'`, `'email_code'`, `'phone_code'`, `'web3_base_signature'`, `'web3_metamask_signature'`, `'web3_coinbase_wallet_signature'` or `'oauth_provider'`.
+ - `'needs_second_factor'`: One of the following [second factor verification strategies](/docs/reference/javascript/sign-in) is missing: `'phone_code'` or `'totp'`.
+ - `'needs_new_password'`: The user needs to set a new password.
+
+ ---
+
+ - `isTransferable`
+ - `boolean`
+
+ Indicates that there is not a matching user for the first-factor verification used, and that the sign-in can be transferred to a sign-up.
+
+ ---
+
+ - `existingSession`
+ - `{ sessionId: string } | undefined`
+
+ TKTKTK
+
+ ---
+
+ - `firstFactorVerification`
+ - [`Verification`](/docs/reference/javascript/types/verification)
+
+ TKTKTK
+
+ ---
+
+ - `secondFactorVerification`
+ - [`Verification`](/docs/reference/javascript/types/verification)
+
+ The second-factor verification for the current sign-in attempt.
+
+ ---
+
+ - `identifier`
+ - `string | null`
+
+ The identifier for the current sign-in attempt.
+
+ ---
+
+ - `createdSessionId`
+ - `string | null`
+
+ The created session ID for the current sign-in attempt.
+
+ ---
+
+ - `userData`
+ - `UserData`
+
+ The user data for the current sign-in attempt.
+[OAuthStrategy](/docs/reference/javascript/types/sso#o-auth-strategy) | 'saml' | 'enterprise\_sso'
+
+ TKTKTK
+
+ ---
+
+ - `redirectUrl?`
+ - `string`
+
+ TKTKTK
+
+ ---
+
+ - `actionCompleteRedirectUrl?`
+ - `string`
+
+ TKTKTK
+
+ ---
+
+ - `transfer?`
+ - `boolean`
+
+ TKTKTK
+
+ ---
+
+ - `ticket?`
+ - `string`
+
+ TKTKTK
+[OAuthStrategy](/docs/reference/javascript/types/sso#o-auth-strategy) | 'saml' | 'enterprise\_sso'
+
+ TKTKTK
+
+ ---
+
+ - `redirectUrl`
+ - `string`
+
+ The URL to redirect to after the user has completed the SSO flow.
+
+ ---
+
+ - `redirectCallbackUrl`
+ - `string`
+
+ TODO @revamp-hooks: This should be handled by FAPI instead.
+