āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā ā š shadcn/directory/clerk/clerk-docs/reference/hooks/use-reverification ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā
Reverification allows you to prompt a user to verify their credentials before performing sensitive actions, even if they're already authenticated. For example, in a banking application, transferring money is considered a "sensitive action." Reverification can be used to confirm the user's identity.
The useReverification() hook is used to handle a session's reverification flow. If a request requires reverification, a modal will display, prompting the user to verify their credentials. Upon successful verification, the original request will automatically retry. If you'd like to build a custom UI, you can use the onNeedsReverification option.
When using reverification, a user's credentials are valid for 10 minutes. Once stale, a user will need to reverify their credentials. This time duration can be customized by using the has() helper on the server-side. See the guide on reverification for more information.
The useReverification() hook displays a prebuilt UI when the user needs to reverify their credentials. You can also build a custom UI to handle the reverification process yourself. Use the following tabs to see examples of either option.
<Tabs items={["Prebuilt UI", "Custom UI"]}> <Tab> <If sdk="expo"> > [!WARNING] > Prebuilt UI is only supported for Expo web apps. For mobile apps, you need to build a custom UI. </If>
### Handle reverification for an action
The following example demonstrates how to use the `useReverification()` hook to require a user to reverify their credentials before being able to update their primary email address. It also demonstrates how to handle the cancellation of the reverification process.
<If sdk="react">
```tsx {{ filename: 'src/components/UpdateUserEmail.tsx', collapsible: true }}
import { useReverification, useUser } from '@clerk/clerk-react'
import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/clerk-react/errors'
export function UpdateUserEmail() {
// Use `useUser()` to get the current user's `User` object
// `User` includes the `update()` method to update the user's primary email address
const { user } = useUser()
// Use `useReverification()` to enhance the `update()` method
// to handle the reverification process
const changePrimaryEmail = useReverification((emailAddressId: string) =>
user?.update({ primaryEmailAddressId: emailAddressId }),
)
const handleClick = async (emailAddressId: string) => {
try {
await changePrimaryEmail(emailAddressId)
} catch (e) {
// Handle if user cancels the reverification process
if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
console.error('User cancelled reverification', e.code)
}
// Handle other errors
// See https://clerk.com/docs/custom-flows/error-handling
// for more info on error handling
console.error(JSON.stringify(e, null, 2))
}
}
return (
<div>
<span>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</span>
<ul>
{user?.emailAddresses.map((email) => (
<li key={email.id}>
<span>{email.emailAddress}</span>
{email.id !== user?.primaryEmailAddress?.id && (
<button onClick={() => handleClick(email.id)}>Make primary</button>
)}
</li>
))}
</ul>
</div>
)
}
```
</If>
<If sdk="nextjs">
```tsx {{ filename: 'components/UpdateUserEmail.tsx', collapsible: true }}
'use client'
import { useReverification, useUser } from '@clerk/nextjs'
import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/nextjs/errors'
export function UpdateUserEmail() {
// Use `useUser()` to get the current user's `User` object
// `User` includes the `update()` method to update the user's primary email address
const { user } = useUser()
// Use `useReverification()` to enhance the `update()` method
// to handle the reverification process
const changePrimaryEmail = useReverification((emailAddressId: string) =>
user?.update({ primaryEmailAddressId: emailAddressId }),
)
const handleClick = async (emailAddressId: string) => {
try {
await changePrimaryEmail(emailAddressId)
} catch (e) {
// Handle if user cancels the reverification process
if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
console.error('User cancelled reverification', e.code)
}
// Handle other errors
// See https://clerk.com/docs/custom-flows/error-handling
// for more info on error handling
console.error(JSON.stringify(e, null, 2))
}
}
return (
<div>
<span>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</span>
<ul>
{user?.emailAddresses.map((email) => (
<li key={email.id}>
<span>{email.emailAddress}</span>
{email.id !== user?.primaryEmailAddress?.id && (
<button onClick={() => handleClick(email.id)}>Make primary</button>
)}
</li>
))}
</ul>
</div>
)
}
```
</If>
<If sdk="react-router">
```tsx {{ filename: 'components/UpdateUserEmail.tsx', collapsible: true }}
import { useReverification, useUser } from '@clerk/react-router'
import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
export function UpdateUserEmail() {
// Use `useUser()` to get the current user's `User` object
// `User` includes the `update()` method to update the user's primary email address
const { user } = useUser()
// Use `useReverification()` to enhance the `update()` method
// to handle the reverification process
const changePrimaryEmail = useReverification((emailAddressId: string) =>
user?.update({ primaryEmailAddressId: emailAddressId }),
)
const handleClick = async (emailAddressId: string) => {
try {
await changePrimaryEmail(emailAddressId)
} catch (e) {
// Handle if user cancels the reverification process
if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
console.error('User cancelled reverification', e.code)
}
// Handle other errors
// See https://clerk.com/docs/custom-flows/error-handling
// for more info on error handling
console.error(JSON.stringify(e, null, 2))
}
}
return (
<div>
<span>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</span>
<ul>
{user?.emailAddresses.map((email) => (
<li key={email.id}>
<span>{email.emailAddress}</span>
{email.id !== user?.primaryEmailAddress?.id && (
<button onClick={() => handleClick(email.id)}>Make primary</button>
)}
</li>
))}
</ul>
</div>
)
}
```
</If>
<If sdk="chrome-extension">
```tsx {{ filename: 'components/UpdateUserEmail.tsx', collapsible: true }}
import { useReverification, useUser } from '@clerk/chrome-extension'
import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
export function UpdateUserEmail() {
// Use `useUser()` to get the current user's `User` object
// `User` includes the `update()` method to update the user's primary email address
const { user } = useUser()
// Use `useReverification()` to enhance the `update()` method
// to handle the reverification process
const changePrimaryEmail = useReverification((emailAddressId: string) =>
user?.update({ primaryEmailAddressId: emailAddressId }),
)
const handleClick = async (emailAddressId: string) => {
try {
await changePrimaryEmail(emailAddressId)
} catch (e) {
// Handle if user cancels the reverification process
if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
console.error('User cancelled reverification', e.code)
}
// Handle other errors
// See https://clerk.com/docs/custom-flows/error-handling
// for more info on error handling
console.error(JSON.stringify(e, null, 2))
}
}
return (
<div>
<span>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</span>
<ul>
{user?.emailAddresses.map((email) => (
<li key={email.id}>
<span>{email.emailAddress}</span>
{email.id !== user?.primaryEmailAddress?.id && (
<button onClick={() => handleClick(email.id)}>Make primary</button>
)}
</li>
))}
</ul>
</div>
)
}
```
</If>
<If sdk="remix">
```tsx {{ filename: 'components/UpdateUserEmail.tsx', collapsible: true }}
import { useReverification, useUser } from '@clerk/remix'
import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
export function UpdateUserEmail() {
// Use `useUser()` to get the current user's `User` object
// `User` includes the `update()` method to update the user's primary email address
const { user } = useUser()
// Use `useReverification()` to enhance the `update()` method
// to handle the reverification process
const changePrimaryEmail = useReverification((emailAddressId: string) =>
user?.update({ primaryEmailAddressId: emailAddressId }),
)
const handleClick = async (emailAddressId: string) => {
try {
await changePrimaryEmail(emailAddressId)
} catch (e) {
// Handle if user cancels the reverification process
if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
console.error('User cancelled reverification', e.code)
}
// Handle other errors
// See https://clerk.com/docs/custom-flows/error-handling
// for more info on error handling
console.error(JSON.stringify(e, null, 2))
}
}
return (
<div>
<span>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</span>
<ul>
{user?.emailAddresses.map((email) => (
<li key={email.id}>
<span>{email.emailAddress}</span>
{email.id !== user?.primaryEmailAddress?.id && (
<button onClick={() => handleClick(email.id)}>Make primary</button>
)}
</li>
))}
</ul>
</div>
)
}
```
</If>
<If sdk="tanstack-react-start">
```tsx {{ filename: 'components/UpdateUserEmail.tsx', collapsible: true }}
import { useReverification, useUser } from '@clerk/tanstack-react-start'
import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
export function UpdateUserEmail() {
// Use `useUser()` to get the current user's `User` object
// `User` includes the `update()` method to update the user's primary email address
const { user } = useUser()
// Use `useReverification()` to enhance the `update()` method
// to handle the reverification process
const changePrimaryEmail = useReverification((emailAddressId: string) =>
user?.update({ primaryEmailAddressId: emailAddressId }),
)
const handleClick = async (emailAddressId: string) => {
try {
await changePrimaryEmail(emailAddressId)
} catch (e) {
// Handle if user cancels the reverification process
if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
console.error('User cancelled reverification', e.code)
}
// Handle other errors
// See https://clerk.com/docs/custom-flows/error-handling
// for more info on error handling
console.error(JSON.stringify(e, null, 2))
}
}
return (
<div>
<span>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</span>
<ul>
{user?.emailAddresses.map((email) => (
<li key={email.id}>
<span>{email.emailAddress}</span>
{email.id !== user?.primaryEmailAddress?.id && (
<button onClick={() => handleClick(email.id)}>Make primary</button>
)}
</li>
))}
</ul>
</div>
)
}
```
</If>
<If sdk="expo">
```tsx {{ filename: 'components/UpdateUserEmail.tsx', collapsible: true }}
import { useReverification, useUser } from '@clerk/clerk-expo'
import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
import { Text, View, TouchableOpacity, ScrollView } from 'react-native'
export function UpdateUserEmail() {
// Use `useUser()` to get the current user's `User` object
// `User` includes the `update()` method to update the user's primary email address
const { user } = useUser()
// Use `useReverification()` to enhance the `update()` method
// to handle the reverification process
const changePrimaryEmail = useReverification((emailAddressId: string) =>
user?.update({ primaryEmailAddressId: emailAddressId }),
)
const handleClick = async (emailAddressId: string) => {
try {
await changePrimaryEmail(emailAddressId)
} catch (e) {
// Handle if user cancels the reverification process
if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
console.error('User cancelled reverification', e.code)
}
// Handle other errors
// See https://clerk.com/docs/custom-flows/error-handling
// for more info on error handling
console.error(JSON.stringify(e, null, 2))
}
}
return (
<View>
<Text>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</Text>
<ScrollView>
{user?.emailAddresses.map((email) => (
<View key={user.id}>
<Text>{email.emailAddress}</Text>
{email.id !== user?.primaryEmailAddress?.id && (
<TouchableOpacity onPress={() => handleClick(email.id)}>
<Text>Make primary</Text>
</TouchableOpacity>
)}
</View>
))}
</ScrollView>
</View>
)
}
```
</If>
### Handle reverification for a fetcher function
The following example demonstrates how to use the `useReverification()` hook to enhance a fetcher function that fetches data from a route that requires reverification. For examples on how to set up a route that requires reverification, see the [guide on reverification](/docs/guides/secure/reverification).
<If sdk="react">
```tsx {{ filename: 'src/components/AccountBalance.tsx' }}
import { useAuth, useReverification } from '@clerk/clerk-react'
import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/clerk-react/errors'
import { useState } from 'react'
export function AccountBalance() {
const { getToken } = useAuth()
const [balance, setBalance] = useState<number | null>(null)
const accountBalance = useReverification(async () => {
const response = await fetch('/api/balance', {
headers: {
Authorization: `Bearer ${await getToken()}`,
},
})
return await response.json()
})
const handleClick = async () => {
try {
const accountBalanceResponse = await accountBalance()
setBalance(accountBalanceResponse.amount)
} catch (e) {
// Handle if user cancels the reverification process
if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
console.error('User cancelled reverification', e.code)
}
// Handle other errors
// See https://clerk.com/docs/custom-flows/error-handling
// for more info on error handling
console.error('Error fetching account balance', e)
}
}
return (
<div>
<span>Your account balance is {balance ? `$${balance}` : '$******'}</span>
<button onClick={() => handleClick()}>See account balance</button>
</div>
)
}
```
</If>
<If sdk="nextjs">
```tsx {{ filename: 'components/AccountBalance.tsx' }}
'use client'
import { useReverification } from '@clerk/nextjs'
import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/nextjs/errors'
import { useState } from 'react'
export function AccountBalance() {
const [balance, setBalance] = useState<number | null>(null)
const accountBalance = useReverification(async () => {
const response = await fetch('/api/balance')
return await response.json()
})
const handleClick = async () => {
try {
const accountBalanceResponse = await accountBalance()
setBalance(accountBalanceResponse.amount)
} catch (e) {
// Handle if user cancels the reverification process
if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
console.error('User cancelled reverification', e.code)
}
// Handle other errors
// See https://clerk.com/docs/custom-flows/error-handling
// for more info on error handling
console.error(JSON.stringify(e, null, 2))
}
}
return (
<div>
<span>Your account balance is {balance ? `$${balance}` : '$******'}</span>
<button onClick={() => handleClick()}>See account balance</button>
</div>
)
}
```
</If>
<If sdk="react-router">
```tsx {{ filename: 'components/AccountBalance.tsx' }}
import { useReverification } from '@clerk/react-router'
import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
import { useState } from 'react'
export function AccountBalance() {
const [balance, setBalance] = useState<number | null>(null)
const accountBalance = useReverification(async () => {
const response = await fetch('/api/balance')
return await response.json()
})
const handleClick = async () => {
try {
const accountBalanceResponse = await accountBalance()
setBalance(accountBalanceResponse.amount)
} catch (e) {
// Handle if user cancels the reverification process
if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
console.error('User cancelled reverification', e.code)
}
// Handle other errors
// See https://clerk.com/docs/custom-flows/error-handling
// for more info on error handling
console.error(JSON.stringify(e, null, 2))
}
}
return (
<div>
<span>Your account balance is {balance ? `$${balance}` : '$******'}</span>
<button onClick={() => handleClick()}>See account balance</button>
</div>
)
}
```
</If>
<If sdk="chrome-extension">
```tsx {{ filename: 'components/AccountBalance.tsx' }}
import { useAuth, useReverification } from '@clerk/chrome-extension'
import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
import { useState } from 'react'
export function AccountBalance() {
const { getToken } = useAuth()
const [balance, setBalance] = useState<number | null>(null)
const accountBalance = useReverification(async () => {
const response = await fetch('/api/balance', {
headers: {
Authorization: `Bearer ${await getToken()}`,
},
})
return await response.json()
})
const handleClick = async () => {
try {
const accountBalanceResponse = await accountBalance()
setBalance(accountBalanceResponse.amount)
} catch (e) {
// Handle if user cancels the reverification process
if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
console.error('User cancelled reverification', e.code)
}
// Handle other errors
// See https://clerk.com/docs/custom-flows/error-handling
// for more info on error handling
console.error('Error fetching account balance', e)
}
}
return (
<div>
<span>Your account balance is {balance ? `$${balance}` : '$******'}</span>
<button onClick={() => handleClick()}>See account balance</button>
</div>
)
}
```
</If>
<If sdk="remix">
```tsx {{ filename: 'components/AccountBalance.tsx' }}
import { useReverification } from '@clerk/remix'
import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
import { useState } from 'react'
export function AccountBalance() {
const [balance, setBalance] = useState<number | null>(null)
const accountBalance = useReverification(async () => {
const response = await fetch('/api/balance')
return await response.json()
})
const handleClick = async () => {
try {
const accountBalanceResponse = await accountBalance()
setBalance(accountBalanceResponse.amount)
} catch (e) {
// Handle if user cancels the reverification process
if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
console.error('User cancelled reverification', e.code)
}
// Handle other errors
// See https://clerk.com/docs/custom-flows/error-handling
// for more info on error handling
console.error(JSON.stringify(e, null, 2))
}
}
return (
<div>
<span>Your account balance is {balance ? `$${balance}` : '$******'}</span>
<button onClick={() => handleClick()}>See account balance</button>
</div>
)
}
```
</If>
<If sdk="tanstack-react-start">
```tsx {{ filename: 'src/components/AccountBalance.tsx' }}
import { useReverification } from '@clerk/tanstack-react-start'
import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
import { useState } from 'react'
export function AccountBalance() {
const [balance, setBalance] = useState<number | null>(null)
const accountBalance = useReverification(async () => {
const response = await fetch('/api/balance')
return await response.json()
})
const handleClick = async () => {
try {
const accountBalanceResponse = await accountBalance()
setBalance(accountBalanceResponse.amount)
} catch (e) {
// Handle if user cancels the reverification process
if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
console.error('User cancelled reverification', e.code)
}
// Handle other errors
// See https://clerk.com/docs/custom-flows/error-handling
// for more info on error handling
console.error(JSON.stringify(e, null, 2))
}
}
return (
<div>
<span>Your account balance is {balance ? `$${balance}` : '$******'}</span>
<button onClick={() => handleClick()}>See account balance</button>
</div>
)
}
```
</If>
<If sdk="expo">
```tsx {{ filename: 'components/AccountBalance.tsx' }}
import { useAuth, useReverification } from '@clerk/clerk-expo'
import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
import { useState } from 'react'
import { Text, View, TouchableOpacity } from 'react-native'
export function AccountBalance() {
const { getToken } = useAuth()
const [balance, setBalance] = useState<number | null>(null)
const accountBalance = useReverification(async () => {
const response = await fetch('/api/balance', {
headers: {
Authorization: `Bearer ${await getToken()}`,
},
})
return await response.json()
})
const handleClick = async () => {
try {
const accountBalanceResponse = await accountBalance()
setBalance(accountBalanceResponse.amount)
} catch (e) {
// Handle if user cancels the reverification process
if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
console.error('User cancelled reverification', e.code)
}
// Handle other errors
// See https://clerk.com/docs/custom-flows/error-handling
// for more info on error handling
console.error('Error fetching account balance', e)
}
}
return (
<View>
<Text>Your account balance is {balance ? `$${balance}` : '$******'}</Text>
<TouchableOpacity onPress={() => handleClick()}>
<Text>See account balance</Text>
</TouchableOpacity>
</View>
)
}
```
</If>
</Tab>
<Tab>
The following example demonstrates how to build a custom UI when using the `useReverification()` hook. In the example, the `useReverification()` hook is used to require a user to reverify their credentials before being able to update their primary email address. It requires two components: the `<UpdateUserEmail />` component displays the list of email addresses to choose from and it renders the second component, `<VerificationComponent />`, which handles the reverification process.
The example handles first factor verification using an email code, so you will need to have the [**Email verification code**](/docs/guides/configure/auth-strategies/sign-up-sign-in-options#email) setting enabled for your application. But you can adapt this example to handle any type of verification level or strategy.
<Tabs items={["UpdateUserEmail", "VerificationComponent"]}>
<Tab>
The `<UpdateUserEmail />` component uses `useReverification()` to enhance the `update()` method, requiring the user to reverify their credentials before being able to update their primary email address.
The `useReverification()` hook provides the `onNeedsReverification` option, which is a handler for building a custom UI. It provides four properties: `level`, `complete`, `cancel`, and `inProgress`. The example tracks these using the `verificationState` state variable.
- The `level` property determines the verification level required for the reverification process. This example only handles first factor verification, which is done in the `<VerificationComponent>` component.
- The `complete` and `cancel` properties are the steps of the reverification process, which is also done in the `<VerificationComponent>` component.
- The `inProgress` property is used to track the state of the reverification process. When the user selects the "Make primary" button, it triggers the reverification process and sets the `inProgress` property is `true`, which displays the `<VerificationComponent>` component.
<If sdk="react">
```tsx {{ filename: 'src/components/UpdateUserEmail.tsx', collapsible: true }}
import { useReverification, useUser } from '@clerk/clerk-react'
import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/clerk-react/errors'
import { useState } from 'react'
import { SessionVerificationLevel } from '@clerk/types'
import { VerificationComponent } from './VerificationComponent'
export function UpdateUserEmail() {
// Use `useUser()` to get the current user's `User` object
// `User` includes the `update()` method to update the user's primary email address
const { user } = useUser()
// TODO: Update to use exported type once available
const [verificationState, setVerificationState] = useState<
| {
complete: () => void
cancel: () => void
level: SessionVerificationLevel | undefined
inProgress: boolean
}
| undefined
>(undefined)
// Use `useReverification()` to enhance the `update()` method
// to handle the reverification process
const changePrimaryEmail = useReverification(
(emailAddressId: string) => user?.update({ primaryEmailAddressId: emailAddressId }),
{
onNeedsReverification: ({ complete, cancel, level }) => {
setVerificationState({
complete,
cancel,
level,
inProgress: true,
})
},
},
)
const handleClick = async (emailAddressId: string) => {
try {
await changePrimaryEmail(emailAddressId)
} catch (e) {
// Handle if user cancels the reverification process
if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
console.error('User cancelled reverification', e.code)
}
// Handle other errors
// See https://clerk.com/docs/custom-flows/error-handling
// for more info on error handling
console.error(JSON.stringify(e, null, 2))
}
}
return (
<div>
<span>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</span>
<ul>
{user?.emailAddresses.map((email) => (
<li key={email.id}>
<span>{email.emailAddress}</span>
{user?.primaryEmailAddressId !== email.id && (
<button onClick={() => handleClick(email.id)}>Make primary</button>
)}
</li>
))}
</ul>
{verificationState?.inProgress && (
<VerificationComponent
level={verificationState?.level}
onComplete={() => {
verificationState.complete()
setVerificationState(undefined)
}}
onCancel={() => {
verificationState.cancel()
setVerificationState(undefined)
}}
/>
)}
</div>
)
}
```
</If>
<If sdk="nextjs">
```tsx {{ filename: 'components/UpdateUserEmail.tsx', collapsible: true }}
'use client'
import { useReverification, useUser } from '@clerk/nextjs'
import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/nextjs/errors'
import { useState } from 'react'
import { SessionVerificationLevel } from '@clerk/types'
import { VerificationComponent } from './VerificationComponent'
export function UpdateUserEmail() {
// Use `useUser()` to get the current user's `User` object
// `User` includes the `update()` method to update the user's primary email address
const { user } = useUser()
// TODO: Update to use exported type once available
const [verificationState, setVerificationState] = useState<
| {
complete: () => void
cancel: () => void
level: SessionVerificationLevel | undefined
inProgress: boolean
}
| undefined
>(undefined)
// Use `useReverification()` to enhance the `update()` method
// to handle the reverification process
const changePrimaryEmail = useReverification(
(emailAddressId: string) => user?.update({ primaryEmailAddressId: emailAddressId }),
{
onNeedsReverification: ({ complete, cancel, level }) => {
setVerificationState({
complete,
cancel,
level,
inProgress: true,
})
},
},
)
const handleClick = async (emailAddressId: string) => {
try {
await changePrimaryEmail(emailAddressId)
} catch (e) {
// Handle if user cancels the reverification process
if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
console.error('User cancelled reverification', e.code)
}
// Handle other errors
// See https://clerk.com/docs/custom-flows/error-handling
// for more info on error handling
console.error(JSON.stringify(e, null, 2))
}
}
return (
<div>
<span>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</span>
<ul>
{user?.emailAddresses.map((email) => (
<li key={email.id}>
<span>{email.emailAddress}</span>
{user?.primaryEmailAddressId !== email.id && (
<button onClick={() => handleClick(email.id)}>Make primary</button>
)}
</li>
))}
</ul>
{verificationState?.inProgress && (
<VerificationComponent
level={verificationState?.level}
onComplete={() => {
verificationState.complete()
setVerificationState(undefined)
}}
onCancel={() => {
verificationState.cancel()
setVerificationState(undefined)
}}
/>
)}
</div>
)
}
```
</If>
<If sdk="react-router">
```tsx {{ filename: 'components/UpdateUserEmail.tsx', collapsible: true }}
import { useReverification, useUser } from '@clerk/react-router'
import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
import { useState } from 'react'
import type { SessionVerificationLevel } from '@clerk/types'
import { VerificationComponent } from './VerificationComponent'
export function UpdateUserEmail() {
// Use `useUser()` to get the current user's `User` object
// `User` includes the `update()` method to update the user's primary email address
const { user } = useUser()
// TODO: Update to use exported type once available
const [verificationState, setVerificationState] = useState<
| {
complete: () => void
cancel: () => void
level: SessionVerificationLevel | undefined
inProgress: boolean
}
| undefined
>(undefined)
// Use `useReverification()` to enhance the `update()` method
// to handle the reverification process
const changePrimaryEmail = useReverification(
(emailAddressId: string) => user?.update({ primaryEmailAddressId: emailAddressId }),
{
onNeedsReverification: ({ complete, cancel, level }) => {
setVerificationState({
complete,
cancel,
level,
inProgress: true,
})
},
},
)
const handleClick = async (emailAddressId: string) => {
try {
await changePrimaryEmail(emailAddressId)
} catch (e) {
// Handle if user cancels the reverification process
if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
console.error('User cancelled reverification', e.code)
}
// Handle other errors
// See https://clerk.com/docs/custom-flows/error-handling
// for more info on error handling
console.error(JSON.stringify(e, null, 2))
}
}
return (
<div>
<span>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</span>
<ul>
{user?.emailAddresses.map((email) => (
<li key={email.id}>
<span>{email.emailAddress}</span>
{user?.primaryEmailAddressId !== email.id && (
<button onClick={() => handleClick(email.id)}>Make primary</button>
)}
</li>
))}
</ul>
{verificationState?.inProgress && (
<VerificationComponent
level={verificationState?.level}
onComplete={() => {
verificationState.complete()
setVerificationState(undefined)
}}
onCancel={() => {
verificationState.cancel()
setVerificationState(undefined)
}}
/>
)}
</div>
)
}
```
</If>
<If sdk="chrome-extension">
```tsx {{ filename: 'components/UpdateUserEmail.tsx', collapsible: true }}
import { useReverification, useUser } from '@clerk/chrome-extension'
import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
import { useState } from 'react'
import type { SessionVerificationLevel } from '@clerk/types'
import { VerificationComponent } from './VerificationComponent'
export function UpdateUserEmail() {
// Use `useUser()` to get the current user's `User` object
// `User` includes the `update()` method to update the user's primary email address
const { user } = useUser()
// TODO: Update to use exported type once available
const [verificationState, setVerificationState] = useState<
| {
complete: () => void
cancel: () => void
level: SessionVerificationLevel | undefined
inProgress: boolean
}
| undefined
>(undefined)
// Use `useReverification()` to enhance the `update()` method
// to handle the reverification process
const changePrimaryEmail = useReverification(
(emailAddressId: string) => user?.update({ primaryEmailAddressId: emailAddressId }),
{
onNeedsReverification: ({ complete, cancel, level }) => {
setVerificationState({
complete,
cancel,
level,
inProgress: true,
})
},
},
)
const handleClick = async (emailAddressId: string) => {
try {
await changePrimaryEmail(emailAddressId)
} catch (e) {
// Handle if user cancels the reverification process
if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
console.error('User cancelled reverification', e.code)
}
// Handle other errors
// See https://clerk.com/docs/custom-flows/error-handling
// for more info on error handling
console.error(JSON.stringify(e, null, 2))
}
}
return (
<div>
<span>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</span>
<ul>
{user?.emailAddresses.map((email) => (
<li key={email.id}>
<span>{email.emailAddress}</span>
{user?.primaryEmailAddressId !== email.id && (
<button onClick={() => handleClick(email.id)}>Make primary</button>
)}
</li>
))}
</ul>
{verificationState?.inProgress && (
<VerificationComponent
level={verificationState?.level}
onComplete={() => {
verificationState.complete()
setVerificationState(undefined)
}}
onCancel={() => {
verificationState.cancel()
setVerificationState(undefined)
}}
/>
)}
</div>
)
}
```
</If>
<If sdk="remix">
```tsx {{ filename: 'components/UpdateUserEmail.tsx', collapsible: true }}
import { useReverification, useUser } from '@clerk/remix'
import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
import { useState } from 'react'
import { SessionVerificationLevel } from '@clerk/types'
import { VerificationComponent } from './VerificationComponent'
export function UpdateUserEmail() {
// Use `useUser()` to get the current user's `User` object
// `User` includes the `update()` method to update the user's primary email address
const { user } = useUser()
// TODO: Update to use exported type once available
const [verificationState, setVerificationState] = useState<
| {
complete: () => void
cancel: () => void
level: SessionVerificationLevel | undefined
inProgress: boolean
}
| undefined
>(undefined)
// Use `useReverification()` to enhance the `update()` method
// to handle the reverification process
const changePrimaryEmail = useReverification(
(emailAddressId: string) => user?.update({ primaryEmailAddressId: emailAddressId }),
{
onNeedsReverification: ({ complete, cancel, level }) => {
setVerificationState({
complete,
cancel,
level,
inProgress: true,
})
},
},
)
const handleClick = async (emailAddressId: string) => {
try {
await changePrimaryEmail(emailAddressId)
} catch (e) {
// Handle if user cancels the reverification process
if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
console.error('User cancelled reverification', e.code)
}
// Handle other errors
// See https://clerk.com/docs/custom-flows/error-handling
// for more info on error handling
console.error(JSON.stringify(e, null, 2))
}
}
return (
<div>
<span>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</span>
<ul>
{user?.emailAddresses.map((email) => (
<li key={email.id}>
<span>{email.emailAddress}</span>
{user?.primaryEmailAddressId !== email.id && (
<button onClick={() => handleClick(email.id)}>Make primary</button>
)}
</li>
))}
</ul>
{verificationState?.inProgress && (
<VerificationComponent
level={verificationState?.level}
onComplete={() => {
verificationState.complete()
setVerificationState(undefined)
}}
onCancel={() => {
verificationState.cancel()
setVerificationState(undefined)
}}
/>
)}
</div>
)
}
```
</If>
<If sdk="tanstack-react-start">
```tsx {{ filename: 'components/UpdateUserEmail.tsx', collapsible: true }}
import { useReverification, useUser } from '@clerk/tanstack-react-start'
import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
import { useState } from 'react'
import { SessionVerificationLevel } from '@clerk/types'
import { VerificationComponent } from './VerificationComponent'
export function UpdateUserEmail() {
// Use `useUser()` to get the current user's `User` object
// `User` includes the `update()` method to update the user's primary email address
const { user } = useUser()
// TODO: Update to use exported type once available
const [verificationState, setVerificationState] = useState<
| {
complete: () => void
cancel: () => void
level: SessionVerificationLevel | undefined
inProgress: boolean
}
| undefined
>(undefined)
// Use `useReverification()` to enhance the `update()` method
// to handle the reverification process
const changePrimaryEmail = useReverification(
(emailAddressId: string) => user?.update({ primaryEmailAddressId: emailAddressId }),
{
onNeedsReverification: ({ complete, cancel, level }) => {
setVerificationState({
complete,
cancel,
level,
inProgress: true,
})
},
},
)
const handleClick = async (emailAddressId: string) => {
try {
await changePrimaryEmail(emailAddressId)
} catch (e) {
// Handle if user cancels the reverification process
if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
console.error('User cancelled reverification', e.code)
}
// Handle other errors
// See https://clerk.com/docs/custom-flows/error-handling
// for more info on error handling
console.error(JSON.stringify(e, null, 2))
}
}
return (
<div>
<span>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</span>
<ul>
{user?.emailAddresses.map((email) => (
<li key={email.id}>
<span>{email.emailAddress}</span>
{user?.primaryEmailAddressId !== email.id && (
<button onClick={() => handleClick(email.id)}>Make primary</button>
)}
</li>
))}
</ul>
{verificationState?.inProgress && (
<VerificationComponent
level={verificationState?.level}
onComplete={() => {
verificationState.complete()
setVerificationState(undefined)
}}
onCancel={() => {
verificationState.cancel()
setVerificationState(undefined)
}}
/>
)}
</div>
)
}
```
</If>
<If sdk="expo">
```tsx {{ filename: 'components/UpdateUserEmail.tsx', collapsible: true }}
import { useReverification, useUser } from '@clerk/clerk-expo'
import { isClerkRuntimeError, isReverificationCancelledError } from '@clerk/shared'
import { useState } from 'react'
import { SessionVerificationLevel } from '@clerk/types'
import { VerificationComponent } from './VerificationComponent'
import { Text, View, TouchableOpacity, ScrollView } from 'react-native'
export function UpdateUserEmail() {
// Use `useUser()` to get the current user's `User` object
// `User` includes the `update()` method to update the user's primary email address
const { user } = useUser()
// TODO: Update to use exported type once available
const [verificationState, setVerificationState] = useState<
| {
complete: () => void
cancel: () => void
level: SessionVerificationLevel | undefined
inProgress: boolean
}
| undefined
>(undefined)
// Use `useReverification()` to enhance the `update()` method
// to handle the reverification process
const changePrimaryEmail = useReverification(
(emailAddressId: string) => user?.update({ primaryEmailAddressId: emailAddressId }),
{
onNeedsReverification: ({ complete, cancel, level }) => {
setVerificationState({
complete,
cancel,
level,
inProgress: true,
})
},
},
)
const handleClick = async (emailAddressId: string) => {
try {
await changePrimaryEmail(emailAddressId)
} catch (e) {
// Handle if user cancels the reverification process
if (isClerkRuntimeError(e) && isReverificationCancelledError(e)) {
console.error('User cancelled reverification', e.code)
}
// Handle other errors
// See https://clerk.com/docs/custom-flows/error-handling
// for more info on error handling
console.error(JSON.stringify(e, null, 2))
}
}
return (
<View>
<Text>Your primary email address is {user?.primaryEmailAddress?.emailAddress}</Text>
<ScrollView>
{user?.emailAddresses.map((email) => (
<View key={user.id}>
<Text>{email.emailAddress}</Text>
{user?.primaryEmailAddressId !== email.id && (
<TouchableOpacity onPress={() => handleClick(email.id)}>
<Text>Make primary</Text>
</TouchableOpacity>
)}
</View>
))}
</ScrollView>
{verificationState?.inProgress && (
<VerificationComponent
level={verificationState?.level}
onComplete={() => {
verificationState.complete()
setVerificationState(undefined)
}}
onCancel={() => {
verificationState.cancel()
setVerificationState(undefined)
}}
/>
)}
</View>
)
}
```
</If>
</Tab>
<Tab>
The `<VerificationComponent />` component handles the reverification process. It uses the `level` property to determine the verification level, which is set to `first_factor`. First, it finds the determined starting first factor from the supported first factors. Then, it prepares the first factor verification using the `strategy` (`email_code` in this case) and `emailAddressId` properties. Finally, it attempts to verify the session with email code provided by the user. If the verification is successful, the `onComplete()` handler is called to complete the reverification process.
<If sdk="react">
```tsx {{ filename: 'src/components/VerificationComponent.tsx', collapsible: true }}
import { useEffect, useRef, useState } from 'react'
import { useSession } from '@clerk/clerk-react'
import {
EmailCodeFactor,
SessionVerificationLevel,
SessionVerificationResource,
} from '@clerk/types'
export function VerificationComponent({
level = 'first_factor',
onComplete,
onCancel,
}: {
level: SessionVerificationLevel | undefined
onComplete: () => void
onCancel: () => void
}) {
const { session } = useSession()
const [code, setCode] = useState<string>('')
const reverificationRef = useRef<SessionVerificationResource | undefined>(undefined)
const [determinedStartingFirstFactor, setDeterminedStartingFirstFactor] = useState<
EmailCodeFactor | undefined
>()
useEffect(() => {
if (reverificationRef.current) {
return
}
session?.startVerification({ level }).then(async (response) => {
reverificationRef.current = response
await prepareEmailVerification(response)
})
}, [])
const prepareEmailVerification = async (verificationResource: SessionVerificationResource) => {
// To simplify the example we will only handle the first factor verification
if (verificationResource.status === 'needs_first_factor') {
// Determine the starting first factor from the supported first factors
const determinedStartingFirstFactor = verificationResource.supportedFirstFactors?.filter(
(factor) => factor.strategy === 'email_code',
)[0]
if (determinedStartingFirstFactor) {
setDeterminedStartingFirstFactor(determinedStartingFirstFactor)
// Prepare the first factor verification with the determined starting first factor
await session?.prepareFirstFactorVerification({
strategy: determinedStartingFirstFactor.strategy,
emailAddressId: determinedStartingFirstFactor?.emailAddressId,
})
}
}
}
const handleVerificationAttempt = async () => {
try {
// Attempt to verify the session with the provided code
await session?.attemptFirstFactorVerification({
strategy: 'email_code',
code,
})
onComplete()
} catch (e) {
// Any error from the attempt to verify the session can be handled here
console.error('Error verifying session', e)
}
}
if (!determinedStartingFirstFactor) {
return null
}
return (
<div>
<p>Enter verification code sent to {determinedStartingFirstFactor.safeIdentifier || ''}</p>
<input type="number" name="code" onChange={(e) => setCode(e.target.value)} />
<button onClick={async () => handleVerificationAttempt()}>Complete</button>
<button onClick={() => onCancel()}>Cancel</button>
</div>
)
}
```
</If>
<If sdk="nextjs">
```tsx {{ filename: 'components/VerificationComponent.tsx', collapsible: true }}
'use client'
import { useEffect, useRef, useState } from 'react'
import { useSession } from '@clerk/nextjs'
import {
EmailCodeFactor,
SessionVerificationLevel,
SessionVerificationResource,
} from '@clerk/types'
export function VerificationComponent({
level = 'first_factor',
onComplete,
onCancel,
}: {
level: SessionVerificationLevel | undefined
onComplete: () => void
onCancel: () => void
}) {
const { session } = useSession()
const [code, setCode] = useState<string>('')
const reverificationRef = useRef<SessionVerificationResource | undefined>(undefined)
const [determinedStartingFirstFactor, setDeterminedStartingFirstFactor] = useState<
EmailCodeFactor | undefined
>()
useEffect(() => {
if (reverificationRef.current) {
return
}
session?.startVerification({ level }).then(async (response) => {
reverificationRef.current = response
await prepareEmailVerification(response)
})
}, [])
const prepareEmailVerification = async (verificationResource: SessionVerificationResource) => {
// To simplify the example we will only handle the first factor verification
if (verificationResource.status === 'needs_first_factor') {
// Determine the starting first factor from the supported first factors
const determinedStartingFirstFactor = verificationResource.supportedFirstFactors?.filter(
(factor) => factor.strategy === 'email_code',
)[0]
if (determinedStartingFirstFactor) {
setDeterminedStartingFirstFactor(determinedStartingFirstFactor)
// Prepare the first factor verification with the determined starting first factor
await session?.prepareFirstFactorVerification({
strategy: determinedStartingFirstFactor.strategy,
emailAddressId: determinedStartingFirstFactor?.emailAddressId,
})
}
}
}
const handleVerificationAttempt = async () => {
try {
// Attempt to verify the session with the provided code
await session?.attemptFirstFactorVerification({
strategy: 'email_code',
code,
})
onComplete()
} catch (e) {
// Any error from the attempt to verify the session can be handled here
console.error('Error verifying session', e)
}
}
if (!determinedStartingFirstFactor) {
return null
}
return (
<div>
<p>Enter verification code sent to {determinedStartingFirstFactor.safeIdentifier || ''}</p>
<input type="number" name="code" onChange={(e) => setCode(e.target.value)} />
<button onClick={async () => handleVerificationAttempt()}>Complete</button>
<button onClick={() => onCancel()}>Cancel</button>
</div>
)
}
```
</If>
<If sdk="react-router">
```tsx {{ filename: 'components/VerificationComponent.tsx', collapsible: true }}
import { useEffect, useRef, useState } from 'react'
import { useSession } from '@clerk/react-router'
import type {
EmailCodeFactor,
SessionVerificationLevel,
SessionVerificationResource,
} from '@clerk/types'
export function VerificationComponent({
level = 'first_factor',
onComplete,
onCancel,
}: {
level: SessionVerificationLevel | undefined
onComplete: () => void
onCancel: () => void
}) {
const { session } = useSession()
const [code, setCode] = useState<string>('')
const reverificationRef = useRef<SessionVerificationResource | undefined>(undefined)
const [determinedStartingFirstFactor, setDeterminedStartingFirstFactor] = useState<
EmailCodeFactor | undefined
>()
useEffect(() => {
if (reverificationRef.current) {
return
}
session?.startVerification({ level }).then(async (response) => {
reverificationRef.current = response
await prepareEmailVerification(response)
})
}, [])
const prepareEmailVerification = async (verificationResource: SessionVerificationResource) => {
// To simplify the example we will only handle the first factor verification
if (verificationResource.status === 'needs_first_factor') {
// Determine the starting first factor from the supported first factors
const determinedStartingFirstFactor = verificationResource.supportedFirstFactors?.filter(
(factor) => factor.strategy === 'email_code',
)[0]
if (determinedStartingFirstFactor) {
setDeterminedStartingFirstFactor(determinedStartingFirstFactor)
// Prepare the first factor verification with the determined starting first factor
await session?.prepareFirstFactorVerification({
strategy: determinedStartingFirstFactor.strategy,
emailAddressId: determinedStartingFirstFactor?.emailAddressId,
})
}
}
}
const handleVerificationAttempt = async () => {
try {
// Attempt to verify the session with the provided code
await session?.attemptFirstFactorVerification({
strategy: 'email_code',
code,
})
onComplete()
} catch (e) {
// Any error from the attempt to verify the session can be handled here
console.error('Error verifying session', e)
}
}
if (!determinedStartingFirstFactor) {
return null
}
return (
<div>
<p>Enter verification code sent to {determinedStartingFirstFactor.safeIdentifier || ''}</p>
<input type="number" name="code" onChange={(e) => setCode(e.target.value)} />
<button onClick={async () => handleVerificationAttempt()}>Complete</button>
<button onClick={() => onCancel()}>Cancel</button>
</div>
)
}
```
</If>
<If sdk="chrome-extension">
```tsx {{ filename: 'components/VerificationComponent.tsx', collapsible: true }}
import { useEffect, useRef, useState } from 'react'
import { useSession } from '@clerk/chrome-extension'
import type {
EmailCodeFactor,
SessionVerificationLevel,
SessionVerificationResource,
} from '@clerk/types'
export function VerificationComponent({
level = 'first_factor',
onComplete,
onCancel,
}: {
level: SessionVerificationLevel | undefined
onComplete: () => void
onCancel: () => void
}) {
const { session } = useSession()
const [code, setCode] = useState<string>('')
const reverificationRef = useRef<SessionVerificationResource | undefined>(undefined)
const [determinedStartingFirstFactor, setDeterminedStartingFirstFactor] = useState<
EmailCodeFactor | undefined
>()
useEffect(() => {
if (reverificationRef.current) {
return
}
session?.startVerification({ level }).then(async (response) => {
reverificationRef.current = response
await prepareEmailVerification(response)
})
}, [])
const prepareEmailVerification = async (verificationResource: SessionVerificationResource) => {
// To simplify the example we will only handle the first factor verification
if (verificationResource.status === 'needs_first_factor') {
// Determine the starting first factor from the supported first factors
const determinedStartingFirstFactor = verificationResource.supportedFirstFactors?.filter(
(factor) => factor.strategy === 'email_code',
)[0]
if (determinedStartingFirstFactor) {
setDeterminedStartingFirstFactor(determinedStartingFirstFactor)
// Prepare the first factor verification with the determined starting first factor
await session?.prepareFirstFactorVerification({
strategy: determinedStartingFirstFactor.strategy,
emailAddressId: determinedStartingFirstFactor?.emailAddressId,
})
}
}
}
const handleVerificationAttempt = async () => {
try {
// Attempt to verify the session with the provided code
await session?.attemptFirstFactorVerification({
strategy: 'email_code',
code,
})
onComplete()
} catch (e) {
// Any error from the attempt to verify the session can be handled here
console.error('Error verifying session', e)
}
}
if (!determinedStartingFirstFactor) {
return null
}
return (
<div>
<p>Enter verification code sent to {determinedStartingFirstFactor.safeIdentifier || ''}</p>
<input type="number" name="code" onChange={(e) => setCode(e.target.value)} />
<button onClick={async () => handleVerificationAttempt()}>Complete</button>
<button onClick={() => onCancel()}>Cancel</button>
</div>
)
}
```
</If>
<If sdk="remix">
```tsx {{ filename: 'components/VerificationComponent.tsx', collapsible: true }}
import { useEffect, useRef, useState } from 'react'
import { useSession } from '@clerk/remix'
import {
EmailCodeFactor,
SessionVerificationLevel,
SessionVerificationResource,
} from '@clerk/types'
export function VerificationComponent({
level = 'first_factor',
onComplete,
onCancel,
}: {
level: SessionVerificationLevel | undefined
onComplete: () => void
onCancel: () => void
}) {
const { session } = useSession()
const [code, setCode] = useState<string>('')
const reverificationRef = useRef<SessionVerificationResource | undefined>(undefined)
const [determinedStartingFirstFactor, setDeterminedStartingFirstFactor] = useState<
EmailCodeFactor | undefined
>()
useEffect(() => {
if (reverificationRef.current) {
return
}
session?.startVerification({ level }).then(async (response) => {
reverificationRef.current = response
await prepareEmailVerification(response)
})
}, [])
const prepareEmailVerification = async (verificationResource: SessionVerificationResource) => {
// To simplify the example we will only handle the first factor verification
if (verificationResource.status === 'needs_first_factor') {
// Determine the starting first factor from the supported first factors
const determinedStartingFirstFactor = verificationResource.supportedFirstFactors?.filter(
(factor) => factor.strategy === 'email_code',
)[0]
if (determinedStartingFirstFactor) {
setDeterminedStartingFirstFactor(determinedStartingFirstFactor)
// Prepare the first factor verification with the determined starting first factor
await session?.prepareFirstFactorVerification({
strategy: determinedStartingFirstFactor.strategy,
emailAddressId: determinedStartingFirstFactor?.emailAddressId,
})
}
}
}
const handleVerificationAttempt = async () => {
try {
// Attempt to verify the session with the provided code
await session?.attemptFirstFactorVerification({
strategy: 'email_code',
code,
})
onComplete()
} catch (e) {
// Any error from the attempt to verify the session can be handled here
console.error('Error verifying session', e)
}
}
if (!determinedStartingFirstFactor) {
return null
}
return (
<div>
<p>Enter verification code sent to {determinedStartingFirstFactor.safeIdentifier || ''}</p>
<input type="number" name="code" onChange={(e) => setCode(e.target.value)} />
<button onClick={async () => handleVerificationAttempt()}>Complete</button>
<button onClick={() => onCancel()}>Cancel</button>
</div>
)
}
```
</If>
<If sdk="tanstack-react-start">
```tsx {{ filename: 'components/VerificationComponent.tsx', collapsible: true }}
import { useEffect, useRef, useState } from 'react'
import { useSession } from '@clerk/tanstack-react-start'
import {
EmailCodeFactor,
SessionVerificationLevel,
SessionVerificationResource,
} from '@clerk/types'
export function VerificationComponent({
level = 'first_factor',
onComplete,
onCancel,
}: {
level: SessionVerificationLevel | undefined
onComplete: () => void
onCancel: () => void
}) {
const { session } = useSession()
const [code, setCode] = useState<string>('')
const reverificationRef = useRef<SessionVerificationResource | undefined>(undefined)
const [determinedStartingFirstFactor, setDeterminedStartingFirstFactor] = useState<
EmailCodeFactor | undefined
>()
useEffect(() => {
if (reverificationRef.current) {
return
}
session?.startVerification({ level }).then(async (response) => {
reverificationRef.current = response
await prepareEmailVerification(response)
})
}, [])
const prepareEmailVerification = async (verificationResource: SessionVerificationResource) => {
// To simplify the example we will only handle the first factor verification
if (verificationResource.status === 'needs_first_factor') {
// Determine the starting first factor from the supported first factors
const determinedStartingFirstFactor = verificationResource.supportedFirstFactors?.filter(
(factor) => factor.strategy === 'email_code',
)[0]
if (determinedStartingFirstFactor) {
setDeterminedStartingFirstFactor(determinedStartingFirstFactor)
// Prepare the first factor verification with the determined starting first factor
await session?.prepareFirstFactorVerification({
strategy: determinedStartingFirstFactor.strategy,
emailAddressId: determinedStartingFirstFactor?.emailAddressId,
})
}
}
}
const handleVerificationAttempt = async () => {
try {
// Attempt to verify the session with the provided code
await session?.attemptFirstFactorVerification({
strategy: 'email_code',
code,
})
onComplete()
} catch (e) {
// Any error from the attempt to verify the session can be handled here
console.error('Error verifying session', e)
}
}
if (!determinedStartingFirstFactor) {
return null
}
return (
<div>
<p>Enter verification code sent to {determinedStartingFirstFactor.safeIdentifier || ''}</p>
<input type="number" name="code" onChange={(e) => setCode(e.target.value)} />
<button onClick={async () => handleVerificationAttempt()}>Complete</button>
<button onClick={() => onCancel()}>Cancel</button>
</div>
)
}
```
</If>
<If sdk="expo">
```tsx {{ filename: 'components/VerificationComponent.tsx', collapsible: true }}
import { useEffect, useRef, useState } from 'react'
import { useSession } from '@clerk/clerk-expo'
import {
EmailCodeFactor,
SessionVerificationLevel,
SessionVerificationResource,
} from '@clerk/types'
import { Text, View, TouchableOpacity, TextInput } from 'react-native'
export function VerificationComponent({
level = 'first_factor',
onComplete,
onCancel,
}: {
level: SessionVerificationLevel | undefined
onComplete: () => void
onCancel: () => void
}) {
const { session } = useSession()
const [code, setCode] = useState<string>('')
const reverificationRef = useRef<SessionVerificationResource | undefined>(undefined)
const [determinedStartingFirstFactor, setDeterminedStartingFirstFactor] = useState<
EmailCodeFactor | undefined
>()
useEffect(() => {
if (reverificationRef.current) {
return
}
session?.startVerification({ level }).then(async (response) => {
reverificationRef.current = response
await prepareEmailVerification(response)
})
}, [])
const prepareEmailVerification = async (verificationResource: SessionVerificationResource) => {
// To simplify the example we will only handle the first factor verification
if (verificationResource.status === 'needs_first_factor') {
// Determine the starting first factor from the supported first factors
const determinedStartingFirstFactor = verificationResource.supportedFirstFactors?.filter(
(factor) => factor.strategy === 'email_code',
)[0]
if (determinedStartingFirstFactor) {
setDeterminedStartingFirstFactor(determinedStartingFirstFactor)
// Prepare the first factor verification with the determined starting first factor
await session?.prepareFirstFactorVerification({
strategy: determinedStartingFirstFactor.strategy,
emailAddressId: determinedStartingFirstFactor?.emailAddressId,
})
}
}
}
const handleVerificationAttempt = async () => {
try {
// Attempt to verify the session with the provided code
await session?.attemptFirstFactorVerification({
strategy: 'email_code',
code,
})
onComplete()
} catch (e) {
// Any error from the attempt to verify the session can be handled here
console.error('Error verifying session', e)
}
}
if (!determinedStartingFirstFactor) {
return null
}
return (
<View>
<Text>
Enter verification code sent to {determinedStartingFirstFactor.safeIdentifier || ''}
</Text>
<TextInput
keyboardType="numeric"
value={code}
onChangeText={setCode}
placeholder="Enter code"
/>
<TouchableOpacity onPress={async () => handleVerificationAttempt()}>
<Text>Complete</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => onCancel()}>
<Text>Cancel</Text>
</TouchableOpacity>
</View>
)
}
```
</If>
</Tab>
</Tabs>
</Tab>
</Tabs>
A function that returns a promise.
options?UseReverificationOptionsThe optional options object. </Properties>
UseReverificationOptionsHandler for the reverification process. Opts out of using the default UI. Use this to build a custom UI. </Properties>
NeedsReverificationParametersMarks the reverification process as complete and retries the original request.
cancel() => voidMarks the reverification process as cancelled and rejects the original request.
level"first_factor" | "second_factor" | "multi_factor" | undefinedThe verification level required for the reverification process. </Properties>
ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā