Add reverification for sensitive actions
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.
Clerk's prebuilt components handle reverification for certain sensitive actions, out of the box. However, this guide will show you how to implement reverification for sensitive actions that are unique to your application.
Sensitive actions that require reverification
The following table shows the sensitive actions that require reverification along with the required strategy and timeframe for each action. When using Clerk's prebuilt components, reverification is automatically handled for these actions.
Caveats
Before enabling this feature, consider the following:
- Available factors for reverification: Not all authentication factors are supported for reverification. Users can only reverify their credentials using the following factors:
- : password, email code, phone code
- : phone code, authenticator app, backup code
- Graceful downgrade of verification level: If you request a
second_factorormulti_factorlevel of verification but the user lacks a second factor available, the utilities automatically downgrade the requested level tofirst_factor. - Eligibility for sensitive actions: Users without any of the above factors cannot reverify. This can be an issue for apps that don't require email addresses to sign up or have disabled email codes in favor of email links.
How to require reverification
If you are performing a sensitive action on the client-side, the useReverification() hook will check if the user has verified their credentials within 10 minutes (the default) and if not, displays a modal that prompts the user to verify their credentials. See the reference doc for detailed examples.
If you are performing a sensitive action on the server side, use the auth.has() helper to check if the user has verified their credentials within a specific time period. This only does the check on the server side, so you still need to handle reverification on the client side in order for your users to be able to verify their credentials.
Example: Reverification in an API endpoint
To handle reverification on the server-side, use the auth.has() helper to check if the user has verified their credentials within a specific time period. Pass a configuration to set the time period you would like. You can pass one of the following configurations: strict_mfa, strict, moderate, and lax. See the reference doc for details on each configuration.
If the user hasn't verified their credentials within that time period, return reverificationErrorResponse to trigger the reverification flow.
In the following example, your /api/reverification-example endpoint will check if a user has verified their credentials within the past 10 minutes. If they haven't, the endpoint returns the reverificationErrorResponse error, which is a 403 Forbidden error.
import { auth, reverificationErrorResponse } from '@clerk/nextjs/server'
// If your SDK doesn't export `reverificationErrorResponse`, import it from `@clerk/shared/authorization-errors`
import { NextResponse } from 'next/server'
export const POST = async () => {
// The `Auth` object gives you access to properties like `isAuthenticated` and `userId`
// Accessing the `Auth` object differs depending on the SDK you're using
// https://clerk.com/docs/reference/backend/types/auth-object#how-to-access-the-auth-object
const { has } = await auth()
// Check if the user has *not* verified their credentials within the past 10 minutes.
const shouldUserRevalidate = !has({ reverification: 'strict' })
// If the user hasn't reverified, return an error with the matching configuration (e.g., `strict`)
if (shouldUserRevalidate) {
return reverificationErrorResponse('strict')
}
// If the user has verified credentials, return a successful response
return NextResponse.json({ success: true })
}To handle reverification on the server-side, use the require_reverification! controller helper. It halts the request and responds with a 403 Forbidden error that the client reads to initiate the reverification flow. Pass a Clerk::StepUp::Preset constant — one of STRICT_MFA, STRICT, MODERATE, or LAX — to set the time period.
class HomeController < ApplicationController
before_action :require_moderate_reverification!, only: :foo
def foo
render json: { success: "true" }
end
private
def require_moderate_reverification!
require_reverification!(Clerk::StepUp::Preset::MODERATE)
end
endAfter setting up reverification on the server-side, you must handle reverification on the client-side so that your users can verify their credentials. Wrap your call to the endpoint in the useReverification() hook to detect the reverificationErrorResponse error and display a modal that allows the user to verify their identity. Upon successful verification, the previously failed request is automatically retried.
In the following example, when the user selects the Transfer button, the transferMoney() function is called, which in turn calls the API endpoint you set up earlier. In the endpoint, you set up reverification to check if the user has verified their credentials within the past 10 minutes. If they haven't, the endpoint returns a 403 forbidden error to the client, triggering the useReverification() hook to display a modal that allows the user to verify their identity. Once the user completes the reverification in the modal, the previously failed request is automatically retried.
'use client'
import { useReverification } from '@clerk/nextjs'
export default function Page({ amount_in_cents }: { amount_in_cents: number }) {
const transferMoney = useReverification(
async () =>
await fetch('/api/reverification-example', {
method: 'POST',
body: JSON.stringify({ amount_in_cents }),
}),
)
return <button onClick={transferMoney}>Transfer</button>
}import { useReverification } from '@clerk/react'
export function TransferButton({ amount_in_cents }: { amount_in_cents: number }) {
const transferMoney = useReverification(
async () =>
await fetch('/api/reverification-example', {
method: 'POST',
body: JSON.stringify({ amount_in_cents }),
}),
)
return <button onClick={transferMoney}>Transfer</button>
}Example: Reverification in a Server Action
You can also perform a reverification check in a Server Action.
'use server'
import { auth, reverificationError } from '@clerk/nextjs/server'
export const myAction = async () => {
const { has } = await auth.protect()
// Check if the user has *not* verified their credentials within the past 10 minutes
const shouldUserRevalidate = !has({ reverification: 'strict' })
// If the user hasn't reverified, return an error with the matching configuration (e.g. `strict`)
if (shouldUserRevalidate) {
return reverificationError('strict')
}
// If the user has verified credentials, return a successful response
return { success: true }
}Then, wrap your call to the Server Action in the useReverification() hook to detect the reverificationError error and display a modal that allows the user to verify their identity. Upon successful verification, the previously failed request is automatically retried.
'use client'
import { useReverification } from '@clerk/nextjs'
import { myAction } from '../actions'
export default function Page() {
const performAction = useReverification(myAction)
const handleClick = async () => {
const myData = await performAction()
// If `myData` is null, the user cancelled the reverification process
// You can choose how your app responds. This example returns null.
if (!myData) return
}
return <button onClick={handleClick}>Perform action</button>
}Feedback
Last updated on