From 5b19f54b2a6b2dad986b7b3f862ef577fc3543d8 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Wed, 27 Nov 2024 21:15:22 +0200 Subject: [PATCH] feat: Introduce Reverification (#1726) Co-authored-by: Bryce Kalow Co-authored-by: Colin Sidoti <51144033+colinclerk@users.noreply.github.com> Co-authored-by: Alexis Aguilar <98043211+alexisintech@users.noreply.github.com> Co-authored-by: victoria --- .../reverification-route-handler.mdx | 20 ++ docs/guides/reverification.mdx | 202 ++++++++++++++++++ docs/manifest.json | 15 ++ docs/references/javascript/session.mdx | 144 ++++++++++++- .../javascript/types/session-verification.mdx | 57 +++++ docs/references/nextjs/auth-object.mdx | 158 ++++++++++++-- docs/references/react/use-reverification.mdx | 100 +++++++++ 7 files changed, 675 insertions(+), 21 deletions(-) create mode 100644 docs/_partials/reverification-route-handler.mdx create mode 100644 docs/guides/reverification.mdx create mode 100644 docs/references/javascript/types/session-verification.mdx create mode 100644 docs/references/react/use-reverification.mdx diff --git a/docs/_partials/reverification-route-handler.mdx b/docs/_partials/reverification-route-handler.mdx new file mode 100644 index 0000000000..acdc4f0248 --- /dev/null +++ b/docs/_partials/reverification-route-handler.mdx @@ -0,0 +1,20 @@ +The following example uses the [`has()`](/docs/references/nextjs/auth-object#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. + +```tsx {{ filename: '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 }) +} +``` diff --git a/docs/guides/reverification.mdx b/docs/guides/reverification.mdx new file mode 100644 index 0000000000..31235d932d --- /dev/null +++ b/docs/guides/reverification.mdx @@ -0,0 +1,202 @@ +--- +title: Add reverification for sensitive actions +description: Learn how to implement Clerk's reverification feature to protect sensitive actions in your application. +--- + +> [!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()`](/docs/references/nextjs/auth-object#has) helper to check if the user has verified their credentials within a specific time period. Pass a [configuration](#supported-verification-configurations) 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()`](/docs/references/nextjs/auth-object#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. + + ```ts {{ filename: '/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 `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. + + ```ruby {{ filename: '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`](https://developer.mozilla.org/en-US/docs/Web/API/Response), use the `reverificationErrorResponse` to trigger reverification. For example: + + ```typescript {{ filename: '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: + + ```typescript {{ filename: '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): + ```json + { + "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()`](/docs/references/react/use-reverification) 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. + + + + ```tsx {{ filename: '/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 + } + ``` + + + + ```tsx {{ filename: '/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 + } + ``` + + + + ```tsx {{ filename: '/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 + } + ``` + + + +## 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](/docs/references/nextjs/auth-object#check-authorization-params-with-custom-permissions) 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 +1. **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`. +1. **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. diff --git a/docs/manifest.json b/docs/manifest.json index bf68051853..9d16391a8c 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -119,6 +119,11 @@ "title": "Architecture scenarios", "href": "/docs/guides/architecture-scenarios" }, + { + "title": "Add reverification for sensitive actions", + "tag": "(Beta)", + "href": "/docs/guides/reverification" + }, { "title": "Routing in Clerk", "href": "/docs/guides/routing" @@ -1919,6 +1924,12 @@ "title": "`useOrganizationList()`", "wrap": false, "href": "/docs/references/react/use-organization-list" + }, + { + "title": "`useReverification()`", + "tag": "(Beta)", + "wrap": false, + "href": "/docs/references/react/use-reverification" } ] ] @@ -2181,6 +2192,10 @@ "title": "SessionStatus", "href": "/docs/references/javascript/types/session-status" }, + { + "title": "SessionVerification", + "href": "/docs/references/javascript/types/session-verification" + }, { "title": "SignInFirstFactor", "href": "/docs/references/javascript/types/sign-in-first-factor" diff --git a/docs/references/javascript/session.mdx b/docs/references/javascript/session.mdx index 0b2e7bb766..7d6665afec 100644 --- a/docs/references/javascript/session.mdx +++ b/docs/references/javascript/session.mdx @@ -1,5 +1,5 @@ --- -title: '`Session`' +title: Session description: The Session object is an abstraction over an HTTP session. It models the period of information exchange between a user and the server. --- @@ -96,6 +96,13 @@ All sessions that are **expired**, **removed**, **replaced**, **ended** or **aba --- + - `factorVerificationAge` + - `[number, number] | null` + + An array where each item represents the number of minutes since the last verification of a first or second factor: `[firstFactorAge, secondFactorAge]`. + + --- + - `actor` - `ActJWTClaim | null` @@ -238,6 +245,141 @@ type CheckAuthorizationParams = - `string` Accepts [permission](/docs/organizations/roles-permissions#permissions) key + + --- + + - `reverification?` + - [ReverificationConfig](/docs/references/nextjs/auth-object#reverification-config) + + The reverification configuration to check for. +### `startVerification()` + +Initiates the reverification flow. Returns a [`SessionVerification`](/docs/references/javascript/types/session-verification) instance with its status and supported factors. + +```typescript +function startVerification(params: SessionVerifyCreateParams): Promise +``` + +#### `SessionVerifyCreateParams` + +```typescript +type SessionVerifyCreateParams = { + status: 'pending' | 'verified' | 'failed' +} +``` + +### `prepareFirstFactorVerification()` + +Initiates the first factor verification process. This is a required step to complete a reverification flow when using a preparable factor. Returns a [`SessionVerification`](/docs/references/javascript/types/session-verification) instance with its status and supported factors. + +```typescript +function prepareFirstFactorVerification( + params: SessionVerifyPrepareFirstFactorParams, +): Promise +``` + +#### `SessionVerifyPrepareFirstFactorParams` + +```ts +type SessionVerifyPrepareFirstFactorParams = EmailCodeConfig | PhoneCodeConfig + +type EmailCodeConfig = { + strategy: 'email_code' + primary?: boolean | undefined + emailAddressId: string +} + +type PhoneCodeConfig = { + strategy: 'phone_code' + phoneNumberId: string + primary?: boolean + default?: boolean +} +``` + +### `attemptFirstFactorVerification()` + +Attempts to complete the first factor verification process. Returns a [`SessionVerification`](/docs/references/javascript/types/session-verification) instance with its status and supported factors. + +```typescript +function attemptFirstFactorVerification( + params: SessionVerifyAttemptFirstFactorParams, +): Promise +``` + +#### `SessionVerifyAttemptFirstFactorParams` + +```ts +type SessionVerifyAttemptFirstFactorParams = EmailCodeAttempt | PhoneCodeAttempt | PasswordAttempt + +type EmailCodeAttempt = { + strategy: 'email_code' + code: string +} + +type PhoneCodeAttempt = { + strategy: 'phone_code' + code: string +} + +type PasswordAttempt = { + strategy: 'password' + password: string +} +``` + +### `prepareSecondFactorVerification()` + +Initiates the second factor verification process. This is a required step to complete a reverification flow when using a preparable factor. Returns a [`SessionVerification`](/docs/references/javascript/types/session-verification) instance with its status and supported factors. + +```typescript +function prepareSecondFactorVerification( + params: SessionVerifyPrepareSecondFactorParams, +): Promise +``` + +#### `SessionVerifyPrepareSecondFactorParams` + +```ts +type SessionVerifyPrepareSecondFactorParams = PhoneCodeSecondFactorConfig + +type PhoneCodeSecondFactorConfig = { + strategy: 'phone_code' + phoneNumberId?: string +} +``` + +### `attemptSecondFactorVerification()` + +Attempts to complete the second factor verification process. Returns a [`SessionVerification`](/docs/references/javascript/types/session-verification) instance with its status and supported factors. + +```typescript +function attemptSecondFactorVerification( + params: SessionVerifyAttemptSecondFactorParams, +): Promise +``` + +#### `SessionVerifyAttemptSecondFactorParams` + +```ts +type SessionVerifyAttemptSecondFactorParams = PhoneCodeAttempt | TOTPAttempt | BackupCodeAttempt + +type PhoneCodeAttempt = { + strategy: 'phone_code' + code: string +} + +type TOTPAttempt = { + strategy: 'totp' + code: string +} + +type BackupCodeAttempt = { + strategy: 'backup_code' + code: string +} +``` + [client-ref]: /docs/references/javascript/client diff --git a/docs/references/javascript/types/session-verification.mdx b/docs/references/javascript/types/session-verification.mdx new file mode 100644 index 0000000000..53a10fc5e7 --- /dev/null +++ b/docs/references/javascript/types/session-verification.mdx @@ -0,0 +1,57 @@ +--- +title: SessionVerification +description: The SessionVerification interface represents the state of a session reverification process. +--- + +An interface that represents the state of the session verification process. + +## Properties + + + - `status` + - `'needs_first_factor' | 'needs_second_factor' | 'complete'` + + The current state of the session verification. + + --- + + - `level` + - `'first_factor' | 'second_factor' | 'multi_factor'` + + The requested level of the session verification. + + --- + + - `session` + - [`Session`](/docs/references/javascript/session) + + The `Session` object that the session verification is attached to. + + --- + + - `firstFactorVerification` + - [`Verification`](/docs/references/javascript/types/verification) + + The state of the verification process for the selected first factor. Initially, this property contains an empty `Verification` object, since there is no first factor selected. You need to call the [`prepareFirstFactorVerification()`](/docs/references/javascript/session#prepare-first-factor-verification) method in order to start the verification process. + + --- + + - `secondFactorVerification` + - [`Verification`](/docs/references/javascript/types/verification) + + The state of the verification process for the selected second factor. Initially, this property contains an empty `Verification` object, since there is no second factor selected. For the `phone_code` strategy, you need to call the [`prepareSecondFactorVerification()`](/docs/references/javascript/session#prepare-second-factor-verification) method in order to start the verification process. For the `totp` or `backup_code` strategies, you can directly attempt the verification by calling the [`attemptSecondFactorVerification()`](/docs/references/javascript/session#attempt-second-factor-verification) method. + + --- + + - `supportedFirstFactors` + - [EmailCodeFactor](/docs/references/javascript/types/sign-in-first-factor#email-code-factor)\[] | [PhoneCodeFactor](/docs/references/javascript/types/sign-in-first-factor#phone-code-factor)\[] | [PasswordFactor](/docs/references/javascript/types/sign-in-first-factor#password-factor)\[] + + Array of the first factors that are supported in the current session verification. Each factor contains information about the verification strategy that can be used. + + --- + + - `supportedSecondFactors` + - [TOTPFactor](/docs/references/javascript/types/sign-in-second-factor#totp-factor)\[] | [PhoneCodeFactor](/docs/references/javascript/types/sign-in-first-factor#phone-code-factor)\[] | [BackupCodeFactor](/docs/references/javascript/types/sign-in-second-factor#backup-code-factor)\[] + + Array of the second factors that are supported in the current session verification. Each factor contains information about the verification strategy that can be used. + diff --git a/docs/references/nextjs/auth-object.mdx b/docs/references/nextjs/auth-object.mdx index ec8e995975..8fc64f6ae9 100644 --- a/docs/references/nextjs/auth-object.mdx +++ b/docs/references/nextjs/auth-object.mdx @@ -3,7 +3,7 @@ title: '`Auth` object' description: The Auth object contains information about the current user's session. --- -Both [`auth()`](/docs/references/nextjs/auth) and [`getAuth()`](/docs/references/nextjs/get-auth) return an `Auth` object. This JavaScript object contains important information like the current user's session ID, user ID, and organization ID. It also contains methods to check for permissions and retrieve the current user's session token. +The `Auth` object contains important information like the current user's session ID, user ID, and organization ID. It also contains methods to check for permissions and retrieve the current user's session token. It's returned by the [`useAuth()`](/docs/references/react/use-auth) hook, the [`auth()`](/docs/references/nextjs/auth) and [`getAuth()`](/docs/references/nextjs/get-auth) helpers, and the `request` object in server contexts. ## `Auth` object properties @@ -71,6 +71,13 @@ Both [`auth()`](/docs/references/nextjs/auth) and [`getAuth()`](/docs/reference --- + - `factorVerificationAge` + - `[number, number] | null` + + An array where each item represents the number of minutes since the last verification of a first or second factor: `[firstFactorAge, secondFactorAge]`. + + --- + - [`getToken()`](#get-token) - [`ServerGetToken`](#server-get-token) @@ -98,15 +105,7 @@ The `orgPermissions` property on the `Auth` object has the type `OrganizationCus ### `has()` -`has()` can be used on both the frontend and the backend to determine if the user _has_ a role or permission. - -You can use `has()` anywhere Clerk returns an [`Auth`](/docs/references/nextjs/auth-object) object: - -- [`auth()`](/docs/references/nextjs/auth) in Next.js App Router -- [`useAuth()`](/docs/references/react/use-auth) in Client Components, including during SSR -- [`getAuth(request)`](/docs/references/nextjs/get-auth) in server contexts outside of the App Router and SSR (Remix Loaders, Node, Express, Fastify, etc) - -`has()` has the following function signature: +`has()` determines if the user _has_ a role or permission. ```ts function has(isAuthorizedParams: CheckAuthorizationParamsWithCustomPermissions): boolean @@ -128,9 +127,69 @@ function has(isAuthorizedParams: CheckAuthorizationParamsWithCustomPermissions): - `string` The permission to check for. + + --- + + - `reverification?` + - [ReverificationConfig](#reverification-config) + + The reverification configuration to check for. This feature is currently in public beta. **It is not recommended for production use**. + + +##### `ReverificationConfig` + +```ts +type ReverificationConfig = + | SessionVerificationTypes + | { + level: SessionVerificationLevel + afterMinutes: SessionVerificationAfterMinutes + } + +type SessionVerificationTypes = 'strict_mfa' | 'strict' | 'moderate' | 'lax' +``` + +The `ReverificationConfig` type has the following properties: + + + - `strict_mfa` + + Requires the user to verify their credentials within the past 10 minutes. If not verified, prompt for both the first and second factors. + + --- + + - `strict` + + Requires the user to verify their credentials within the past 10 minutes. If not verified, prompt for the second factor. + + --- + + - `moderate` + + Requires the user to verify their credentials within the past hour. If not verified, prompt for the second factor. + + --- + + - `lax` + + Requires the user to verify their credentials within the past day. If not verified, prompt for the second factor. + + --- + + - `level` + - `"first_factor" | "second_factor" | "multi_factor"` + + The reverification level of credentials to check for. + + --- + + - `afterMinutes` + - `number` + + The age of the factor level to check for. Value should be greater than or equal to 1 and less than 99,999. -#### `has()` example +#### `has()` permissions example You can use `has()` to check if a user is authorized to access a component. @@ -153,6 +212,15 @@ export default async function Page() { } ``` +#### `has()` reverification example + +> [!IMPORTANT] +> This example demonstrates how to handle reverification **server-side**. For information on how to handle reverification on the **client-side**, see the [guide on reverification](/docs/guides/reverification). + +You can use `has()` to check if a user has verified their credentials within a certain time frame. + + + ### `getToken()` You can use `getToken()` on an `Auth` object to retrieve the user's session token. You can also use this method to retrieve a custom JWT template. @@ -275,8 +343,8 @@ The following is an example of the `Auth` object without an active organization: ```js {{ prettier: false }} { - sessionId: 'sess_2GaMqUCB3Sc1WNAkWuNzsnYVVEy', - userId: 'user_2F2u1wtUyUlxKgFkKqtJNtpJJWj', + sessionId: 'sess_123', + userId: 'user_123', orgId: null, orgRole: null, orgSlug: null, @@ -289,8 +357,8 @@ The following is an example of the `Auth` object without an active organization: iat: 1666622547, iss: 'https://clerk.quiet.muskox-85.lcl.dev', nbf: 1666622537, - sid: 'sess_2GaMqUCB3Sc1WNAkWuNzsnYVVEy', - sub: 'user_2F2u1wtUyUlxKgFkKqtJNtpJJWj', + sid: 'sess_123', + sub: 'user_123', }, } ``` @@ -301,9 +369,9 @@ The following is an example of the `Auth` object with an active organization: ```js {{ prettier: false }} { - sessionId: 'sess_2GaMqUCB3Sc1WNAkWuNzsnYVVEy', - userId: 'user_2F2u1wtUyUlxKgFkKqtJNtpJJWj', - orgId: 'org_2ZVFfVAkt4ocVjHL0KTdL94AhXK', + sessionId: 'sess_123', + userId: 'user_123', + orgId: 'org_123', orgRole: 'org:admin', orgSlug: undefined, orgPermissions: ['org:team_settings:manage'], // Custom permissions @@ -315,8 +383,58 @@ The following is an example of the `Auth` object with an active organization: iat: 1666622547, iss: 'https://clerk.quiet.muskox-85.lcl.dev', nbf: 1666622537, - sid: 'sess_2GaMqUCB3Sc1WNAkWuNzsnYVVEy', - sub: 'user_2F2u1wtUyUlxKgFkKqtJNtpJJWj', + sid: 'sess_123', + sub: 'user_123', + }, +} +``` + +## `Auth` object example with valid factor age + +```js {{ mark: [8], prettier: false }} +{ + sessionId: 'sess_123', + userId: 'user_123', + orgId: null, + orgRole: null, + orgSlug: null, + orgPermissions: null, + factorVerificationAge: [0,0], + has: [Function (anonymous)], + getToken: [AsyncFunction (anonymous)], + claims: { + azp: 'http://localhost:3000', + exp: 1666622607, + iat: 1666622547, + iss: 'https://clerk.quiet.muskox-85.lcl.dev', + nbf: 1666622537, + sid: 'sess_123', + sub: 'user_123', + }, +} +``` + +## `Auth` object example of a user without an MFA method registered + +```js {{ mark: [8], prettier: false }} +{ + sessionId: 'sess_123', + userId: 'user_123', + orgId: null, + orgRole: null, + orgSlug: null, + orgPermissions: null, + factorVerificationAge: [0, -1], + has: [Function (anonymous)], + getToken: [AsyncFunction (anonymous)], + claims: { + azp: 'http://localhost:3000', + exp: 1666622607, + iat: 1666622547, + iss: 'https://clerk.quiet.muskox-85.lcl.dev', + nbf: 1666622537, + sid: 'sess_123', + sub: 'user_123', }, } ``` diff --git a/docs/references/react/use-reverification.mdx b/docs/references/react/use-reverification.mdx new file mode 100644 index 0000000000..5a804acb24 --- /dev/null +++ b/docs/references/react/use-reverification.mdx @@ -0,0 +1,100 @@ +--- +title: '`useReverification()`' +description: Clerk's useReverification() hook enhances a fetcher function to handle a session's reverification flow. +--- + +> [!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-react@5.17.0` or later, and `@clerk/clerk-js@5.35.0` or later. + +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. + +## Parameters + + + - `fetcher` + - `Fetcher extends (...args: any[]) => Promise` + + The fetcher function. + + --- + + - `options?` + - [`UseReverificationOptions`](#use-reverification-options) + + The optional options object. + + +### `UseReverificationOptions` + + + - `onCancel?` + - `() ⇒ void` + + A callback function that is invoked when the user cancels the reverification process. + + --- + + - `throwOnCancel?` + - `boolean` + + Determines if an error should throw when the user cancels the reverification process. Defaults to `false`. + + +## Returns + +The `useReverification()` hook returns an array with the "enhanced" fetcher. + +## How to use the `useReverification()` hook + +### Handle cancellation of the reverification process + +The following example demonstrates how to handle scenarios where a user cancels the reverification flow, such as closing the modal, which might result in `myData` being `null`. + +In this example, `myFetcher` would be a function in your backend that fetches data from the route that requires reverification. See the [guide on how to require reverification](/docs/guides/reverification) for more information. + +```tsx {{ filename: 'MyButton.tsx' }} +import { useReverification } from '@clerk/react' + +export function MyButton() { + const [enhancedFetcher] = useReverification(myFetcher) + + const handleClick = async () => { + const myData = await enhancedFetcher() + // If `myData` is null, the user canceled the reverification process + // You can choose how your app responds. This example returns null. + if (!myData) return + } + + return +} +``` + +### Handle `throwOnCancel` + +When `throwOnCancel` is set to `true`, the fetcher will throw a `ClerkRuntimeError` with the code `"reverification_cancelled"` if the user cancels the reverification flow (for example, by closing the modal). This error can be caught and handled according to your app's needs. For example, by displaying a toast notification to the user or silently ignoring the cancellation. + +In this example, `myFetcher` would be a function in your backend that fetches data from the route that requires reverification. See the [guide on how to require reverification](/docs/guides/reverification) for more information. + +```tsx {{ filename: 'MyButton.tsx' }} +import { useReverification } from '@clerk/clerk-react' +import { isClerkRuntimeError } from '@clerk/clerk-react/errors' + +export function MyButton() { + const [enhancedFetcher] = useReverification(myFetcher, { throwOnCancel: true }) + + const handleClick = async () => { + try { + const myData = await enhancedFetcher() + } catch (e) { + // Handle if user cancels the reverification process + if (isClerkRuntimeError(e) && e.code === 'reverification_cancelled') { + console.error('User cancelled reverification', e.code) + } + } + } + + return +} +```