Docs

Add reverification for sensitive actions

Warning

This feature is currently in public beta. It is not recommended for production use.

Depending on the SDK you're using, this feature requires @clerk/nextjs@6.5.0 or later, @clerk/clerk-sdk-ruby@3.3.0 or later, and @clerk/clerk-js@5.35.0 or later.

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.

How to require reverification

To implement reverification, you need to handle it both on the server- and client-side.

Handle reverification server-side

To handle reverification 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. If the user hasn't verified their credentials within that time period, return either reverificationError or reverificationErrorResponse, depending on the framework you're using. Use the following tabs to see examples for different frameworks.

The following example uses the has() helper to check if the user has verified their credentials within a specific time period. The strict configuration sets the time period to 10 minutes. If the user hasn't verified their credentials within 10 minutes, the reverificationError utility is used to return an error.

/app/actions.ts
'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 }
}

The following example uses the has() helper to check if the user has verified their credentials within a specific time period. The strict configuration sets the time period to 10 minutes. If the user hasn't verified their credentials within 10 minutes, the reverificationErrorResponse utility is used to return an error.

app/api/reverification-example/route.ts
import { auth, reverificationErrorResponse } from '@clerk/nextjs/server'

export const POST = async (req: Request) => {
  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 new Response({ success: true })
}

The following example uses the clerk_user_needs_reverification helper to check if the user has verified their credentials within a specific time period. The moderate configuration sets the time period to 1 hour. If the user hasn't verified their credentials within 1 hour, the clerk_render_reverification utility is used to return a 403 forbidden error that the client reads to initiate the reverification flow. Once the user completes the reverification on the client-side, they can access the foo action, which returns a success response.

app/controllers/home_controller.rb
class HomeController < ApplicationController
  before_action :require_reverification, only: :foo

  def foo
    render json: { success: "true" }
  end

  private

  # will halt the request and respond with a JSON that Clerk.js
  # will read and kickstart a reverification flow
  def require_reverification
    clerk_render_reverification(Clerk::StepUp::PRESETS[:moderate]) if clerk_user_needs_reverification?(Clerk::StepUp::PRESETS[:moderate])
  end
end

Warning

reverificationErrorResponse and reverificationError requires @clerk/shared@2.17.0 or later, and @clerk/clerk-js@5.35.0 or later.

  • For a JavaScript or Typescript framework that supports the Fetch API Response, use the reverificationErrorResponse to trigger reverification. For example:
    src/api/reverification-example.ts
    import type { APIRoute } from 'astro'
    import { reverificationErrorResponse } from '@clerk/shared/authorization-errors'
    
    export const GET: APIRoute = async ({ locals }) => {
      const { has } = locals.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')
      }
    
      return new Response('success', { status: 200 })
    }
  • For a JavaScript or Typescript framework that provides its own utilities to handle responses, use reverificationError. For example:
    src/api/hello.ts
    import { Hono } from 'hono'
    
    const app = new Hono()
    
    // POST request route
    app.post('/api/hello', async (c) => {
      return c.json(reverificationError('strict'))
    })
  • Alternatively, if you're not using JavaScript or TypeScript, you can create a custom helper that returns the following JSON response (it's recommended to use a 403 Forbidden status code in your response):
    {
      "clerk_error": {
        "type": "forbidden",
        "reason": "reverification-error"
      }
    }

Handle reverification client-side

After setting up reverification on the server-side, you must handle reverification on the client-side.

The following example demonstrates how to use the useReverification() hook to detect authorization errors and automatically display a modal that allows the user to verify their identity. Upon successful verification, the previously failed request is automatically retried.

/app/perform-action/page.tsx
'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>
}
/app/transfer/page.tsx
'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>
}
/src/components/TransferButton.js
import { useReverification } from '@clerk/react'

export function TransferButton({ amount_in_cents }: { amount_in_cents: number }) {
  const [transferMoney] = useReverification(() =>
    fetch('/api/reverification-example', {
      method: 'POST',
      body: JSON.stringify({ amount_in_cents }),
    }),
  )

  return <button onClick={transferMoney}>Transfer</button>
}

Supported reverification configurations

To define the time period of the reverification check, you can pass the one of the following configurations to the has() helper: strict_mfa, strict, moderate, and lax. See the has() reference doc for more details.

Caveats

Before enabling this feature, consider the following:

  1. Available factors for reverification: Not all authentication factors are supported for reverification. The available options are:
    • First factors: password, email code, phone code
    • Second factors: phone code, authenticator app, backup code
  2. Graceful downgrade of verification level: If you request a second_factor or multi_factor level of verification but the user lacks a second factor available, the utilities automatically downgrade the requested level to first_factor.
  3. 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.

Feedback

What did you think of this content?

Last updated on