Skip to content

Commit a722ab8

Browse files
committed
Intorduces <TaskSetupMfa /> and setup-mfa session task
1 parent feb4f72 commit a722ab8

File tree

6 files changed

+453
-131
lines changed

6 files changed

+453
-131
lines changed

docs/_partials/session-tasks-table.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ The following table lists the available tasks and their corresponding keys.
66
| - | - | - |
77
| [Allow Personal Accounts](/docs/guides/organizations/configure#personal-accounts) | `choose-organization` | Disabled by default when enabling Organizations [for instances created after August 22, 2025](!update). When disabled, users are required to choose an Organization after authenticating. When enabled, users can choose a [Personal Account](!personal-account) instead of an Organization. |
88
| [Force password reset](/docs/guides/secure/password-protection-and-rules#manually-set-a-password-as-compromised) | `reset-password` | Enabled by default [for instances created after December 8, 2025](!update). When enabled, the user is required to reset their password on their next sign-in if their password is marked as compromised. |
9+
| [Multi-factor authentication requirement](/docs/guides/configure/auth-strategies/sign-up-sign-in-options#multi-factor-authentication) | `setup-mfa` | When enabled, users are required to set up multi-factor authentication (MFA) after authenticating. Users can choose between authenticator app (TOTP) or SMS verification depending on which methods are enabled in the instance settings. |

docs/guides/configure/session-tasks.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ The following table lists the available tasks and their corresponding components
3030
| - | - |
3131
| [Personal Accounts disabled (default)](/docs/guides/organizations/configure#enable-organizations) | [`<TaskChooseOrganization />`](/docs/reference/components/authentication/task-choose-organization) |
3232
| [Force password reset](/docs/guides/secure/password-protection-and-rules#manually-set-a-password-as-compromised) | [`<TaskResetPassword />`](/docs/reference/components/authentication/task-reset-password) |
33+
| [Multi-factor authentication requirement](/docs/guides/configure/auth-strategies/sign-up-sign-in-options#multi-factor-authentication) | [`<TaskSetupMfa />`](/docs/reference/components/authentication/task-setup-mfa) |
3334

3435
### No components, no problem
3536

docs/guides/development/custom-flows/authentication/session-tasks.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ This guide demonstrates how to use the Clerk API to build a custom user interfac
3030
| - | - |
3131
| `choose-organization` | [Organization switcher guide](/docs/guides/development/custom-flows/organizations/organization-switcher) |
3232
| `reset-password` | [Reset password guide](/docs/guides/development/custom-flows/account-updates/forgot-password) |
33+
| `setup-mfa` | [Manage TOTP-based MFA guide](/docs/guides/development/custom-flows/account-updates/manage-totp-based-mfa) or [Manage SMS-based MFA guide](/docs/guides/development/custom-flows/account-updates/manage-sms-based-mfa) {/* TODO: Update to setup-mfa custom flow guide once created */} |
3334

3435
## Protect routes
3536

docs/guides/secure/force-mfa.mdx

Lines changed: 151 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -3,169 +3,189 @@ title: Force multi-factor authentication (MFA) for all users
33
description: Learn how to force multi-factor authentication (MFA) for all users in your Clerk application.
44
---
55

6-
By default, Clerk does not enforce [multi-factor authentication (MFA)](/docs/guides/configure/auth-strategies/sign-up-sign-in-options#multi-factor-authentication) for all users. This guide demonstrates how to force MFA for all users by using `clerkMiddleware()` to intercept all requests and check whether a user has MFA enabled. If the user does not have MFA enabled, `clerkMiddleware()` redirects them to the `/mfa` page where they can set up MFA.
6+
Clerk can require all users to set up multi-factor authentication (MFA) after signing in or signing up. This guide demonstrates how to enable this requirement using Clerk's `setup-mfa` [session task](!session-tasks).
77

88
<Steps>
9-
## Enable MFA in the Clerk Dashboard
9+
## Enable MFA strategies
1010

11-
If you haven't already, enable MFA for your users.
11+
First, enable the MFA strategies you want to offer to your users.
1212

1313
1. In the Clerk Dashboard, navigate to the [**Multi-factor**](https://dashboard.clerk.com/~/user-authentication/multi-factor) page.
14-
1. Toggle on the MFA strategies you would like to enable.
14+
1. Toggle on the MFA strategies you would like to enable:
15+
- **Authenticator application (TOTP)**: Users can use apps like Google Authenticator or Authy
16+
- **SMS verification code**: Users receive a one-time code via SMS
1517

16-
## Customize the session token to include the `two_factor_enabled` property
18+
## Enable MFA requirement
1719

18-
Every `User` object has a `two_factor_enabled` property that indicates whether the user has MFA enabled. Store this property in the session token so that you can check it in your `clerkMiddleware()`.
20+
Once you've enabled at least one MFA strategy, you can require all users to set up MFA.
1921

20-
1. In the Clerk Dashboard, navigate to the [**Sessions**](https://dashboard.clerk.com/~/sessions) page.
21-
1. Under **Customize session token**, in the **Claims** editor, enter the following JSON and select **Save**. The key can be any string, but the value must be the `user.two_factor_enabled` property, as shown in the following example. If you have already customized your session token, you may need to merge this with what you currently have.
22+
1. On the same [**Multi-factor**](https://dashboard.clerk.com/~/user-authentication/multi-factor) page, scroll down to the **Require multi-factor authentication** section.
23+
1. Toggle on **Require multi-factor authentication**.
2224

23-
```json
24-
{
25-
"isMfa": "{{user.two_factor_enabled}}"
26-
}
27-
```
25+
When enabled, Clerk automatically creates a `setup-mfa` session task for:
26+
- New users after they sign up
27+
- Existing users without MFA on their next sign-in
2828

29-
## Update `clerkMiddleware()`
29+
## How it works
3030

31-
Update your `clerkMiddleware()` to check if the user has MFA enabled.
31+
Once the MFA requirement is enabled:
3232

33-
<Tabs items={['Next.js', 'Astro', 'Nuxt']}>
33+
1. Users sign in or sign up successfully
34+
1. If they don't have MFA set up, Clerk creates a `setup-mfa` session task
35+
1. The user's session enters a `pending` state and they're treated as signed-out
36+
1. Clerk's [`<SignIn />`](/docs/reference/components/authentication/sign-in) and [`<SignUp />`](/docs/reference/components/authentication/sign-up) components automatically display the [`<TaskSetupMfa />`](/docs/reference/components/authentication/task-setup-mfa) component
37+
1. After the user completes MFA setup, their session becomes `active` and they can access your application
38+
39+
For more information about session tasks, see the [session tasks guide](/docs/guides/configure/session-tasks).
40+
41+
## (Optional) Customize the MFA setup page
42+
43+
By default, the `<SignIn />` and `<SignUp />` components handle the MFA setup flow automatically. However, if you want to customize the route where the MFA setup page is rendered, you can host the `<TaskSetupMfa />` component on a custom page.
44+
45+
<Tabs items={['Next.js', 'React', 'React Router', 'TanStack React Start']}>
3446
<Tab>
35-
<Include src="_partials/nextjs/nextjs-15-callout" />
36-
37-
```tsx {{ filename: 'proxy.ts', collapsible: true }}
38-
import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
39-
import { NextResponse } from 'next/server'
40-
41-
const isMFARoute = createRouteMatcher(['/account/manage-mfa/add(.*)'])
42-
const isSignInRoute = createRouteMatcher(['/sign-in(.*)'])
43-
44-
export default clerkMiddleware(async (auth, req) => {
45-
const { userId, sessionClaims } = await auth()
46-
47-
// Redirect to homepage if the user is signed in and on the sign-in page
48-
if (userId !== null && isSignInRoute(req)) {
49-
return NextResponse.redirect(new URL('/', req.url))
50-
}
51-
52-
// Redirect to MFA setup page if MFA is not enabled
53-
if (userId !== null && !isMFARoute(req)) {
54-
if (sessionClaims.isMfa === undefined) {
55-
console.error('You need to add the `isMfa` claim to your session token.')
56-
}
57-
if (sessionClaims.isMfa === false) {
58-
return NextResponse.redirect(new URL('/account/manage-mfa/add', req.url))
59-
}
60-
}
61-
})
47+
First, configure the `taskUrls` option in your `ClerkProvider` to specify where the MFA setup page is located:
48+
49+
```tsx {{ filename: 'app/layout.tsx', mark: [7] }}
50+
import { ClerkProvider } from '@clerk/nextjs'
51+
52+
export default function RootLayout({ children }: { children: React.ReactNode }) {
53+
return (
54+
<html lang="en">
55+
<body>
56+
<ClerkProvider taskUrls={{ 'setup-mfa': '/onboarding/setup-mfa' }}>
57+
{children}
58+
</ClerkProvider>
59+
</body>
60+
</html>
61+
)
62+
}
63+
```
64+
65+
Then, create the page at the specified route:
66+
67+
```tsx {{ filename: 'app/onboarding/setup-mfa/page.tsx' }}
68+
import { TaskSetupMfa } from '@clerk/nextjs'
6269

63-
export const config = {
64-
matcher: [
65-
// Skip Next.js internals and all static files, unless found in search params
66-
'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
67-
// Always run for API routes
68-
'/(api|trpc)(.*)',
69-
],
70+
export default function Page() {
71+
return <TaskSetupMfa redirectUrlComplete="/dashboard" />
7072
}
7173
```
7274
</Tab>
7375

7476
<Tab>
75-
```tsx {{ filename: 'src/middleware.ts', collapsible: true }}
76-
import { clerkMiddleware, createRouteMatcher } from '@clerk/astro/server'
77-
78-
const isMFARoute = createRouteMatcher(['/account/manage-mfa/add(.*)'])
79-
const isSignInRoute = createRouteMatcher(['/sign-in(.*)'])
80-
81-
export const onRequest = clerkMiddleware((auth, context) => {
82-
const { userId, sessionClaims } = auth()
83-
84-
// Redirect to homepage if the user is signed in and on the sign-in page
85-
if (userId !== null && isSignInRoute(context.request)) {
86-
return Response.redirect(new URL('/', context.request.url))
87-
}
88-
89-
// Redirect to MFA setup page if MFA is not enabled
90-
if (userId !== null && !isMFARoute(context.request)) {
91-
if (sessionClaims.isMfa === undefined) {
92-
console.error('You need to add the `isMfa` claim to your session token.')
93-
}
94-
if (sessionClaims.isMfa === false) {
95-
return Response.redirect(new URL('/account/manage-mfa/add', context.request.url))
96-
}
97-
}
98-
})
77+
First, configure the `taskUrls` option in your `ClerkProvider`:
78+
79+
```tsx {{ filename: 'index.tsx', mark: [17] }}
80+
import React from 'react'
81+
import ReactDOM from 'react-dom/client'
82+
import App from './App.tsx'
83+
import { ClerkProvider } from '@clerk/react'
84+
85+
const PUBLISHABLE_KEY = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY
9986

100-
export const config = {
101-
matcher: [
102-
// Skip Next.js internals and all static files, unless found in search params
103-
'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
104-
// Always run for API routes
105-
'/(api|trpc)(.*)',
106-
],
87+
if (!PUBLISHABLE_KEY) {
88+
throw new Error('Add your Clerk Publishable Key to the .env file')
10789
}
90+
91+
ReactDOM.createRoot(document.getElementById('root')!).render(
92+
<React.StrictMode>
93+
<ClerkProvider
94+
publishableKey={PUBLISHABLE_KEY}
95+
taskUrls={{ 'setup-mfa': '/onboarding/setup-mfa' }}
96+
>
97+
<App />
98+
</ClerkProvider>
99+
</React.StrictMode>,
100+
)
101+
```
102+
103+
Then, create the page component:
104+
105+
```jsx {{ filename: 'src/onboarding/setup-mfa.tsx' }}
106+
import { TaskSetupMfa } from '@clerk/react'
107+
108+
const SetupMfaPage = () => <TaskSetupMfa redirectUrlComplete="/dashboard" />
109+
110+
export default SetupMfaPage
108111
```
109112
</Tab>
110113

111114
<Tab>
112-
By default, the Nuxt SDK automatically adds [the `clerkMiddleware()` helper](/docs/reference/nuxt/clerk-middleware) to your Nuxt application. To manually configure the middleware, in your `nuxt.config.ts` file, under the `clerk` property, set `skipServerMiddleware: true`.
113-
114-
```tsx {{ filename: 'nuxt.config.ts', mark: [[3, 5]] }}
115-
export default defineNuxtConfig({
116-
modules: ['@clerk/nuxt'],
117-
clerk: {
118-
skipServerMiddleware: true,
119-
},
120-
})
115+
First, configure the `taskUrls` option in your `ClerkProvider`:
116+
117+
```tsx {{ filename: 'app/root.tsx', mark: [6] }}
118+
import { ClerkProvider } from '@clerk/react-router'
119+
import { Outlet } from 'react-router'
120+
121+
export default function App() {
122+
return (
123+
<ClerkProvider taskUrls={{ 'setup-mfa': '/onboarding/setup-mfa' }}>
124+
<Outlet />
125+
</ClerkProvider>
126+
)
127+
}
121128
```
122129

123-
Then, in your `server/middleware/clerk.ts` file, add the following code:
124-
125-
```tsx {{ filename: 'server/middleware/clerk.ts', collapsible: true }}
126-
import { clerkMiddleware } from '@clerk/nuxt/server'
127-
128-
export default clerkMiddleware(async (event) => {
129-
const isMFARoute = event.path.startsWith('/account/manage-mfa/add')
130-
const isSignInRoute = event.path.startsWith('/sign-in')
131-
132-
const { userId, sessionClaims } = event.context.auth()
133-
134-
// Redirect to homepage if the user is signed in and on the sign-in page
135-
if (userId !== null && isSignInRoute) {
136-
throw createError({
137-
statusMessage: 'You are already signed in.',
138-
})
139-
}
140-
141-
// Redirect to MFA setup page if MFA is not enabled
142-
if (userId !== null && !isMFARoute) {
143-
if (sessionClaims.isMfa === undefined) {
144-
throw createError({
145-
statusMessage: 'You need to add the `isMfa` claim to your session token.',
146-
})
147-
}
148-
if (sessionClaims.isMfa === false) {
149-
throw createError({
150-
statusMessage: 'You need to enable MFA for your account.',
151-
})
152-
}
153-
}
130+
Then, create the route component:
131+
132+
```tsx {{ filename: 'app/routes/onboarding/setup-mfa.tsx' }}
133+
import { TaskSetupMfa } from '@clerk/react-router'
134+
135+
export default function SetupMfaPage() {
136+
return <TaskSetupMfa redirectUrlComplete="/dashboard" />
137+
}
138+
```
139+
</Tab>
140+
141+
<Tab>
142+
First, configure the `taskUrls` option in your `ClerkProvider`:
143+
144+
```tsx {{ filename: 'src/routes/__root.tsx', mark: [12] }}
145+
import * as React from 'react'
146+
import { HeadContent, Scripts } from '@tanstack/react-router'
147+
import { ClerkProvider } from '@clerk/tanstack-react-start'
148+
149+
function RootDocument({ children }: { children: React.ReactNode }) {
150+
return (
151+
<html>
152+
<head>
153+
<HeadContent />
154+
</head>
155+
<body>
156+
<ClerkProvider taskUrls={{ 'setup-mfa': '/onboarding/setup-mfa' }}>
157+
{children}
158+
</ClerkProvider>
159+
<Scripts />
160+
</body>
161+
</html>
162+
)
163+
}
164+
```
165+
166+
Then, create the route:
167+
168+
```tsx {{ filename: 'src/routes/onboarding/setup-mfa.tsx' }}
169+
import { TaskSetupMfa } from '@clerk/tanstack-react-start'
170+
import { createFileRoute } from '@tanstack/react-router'
171+
172+
export const Route = createFileRoute('/onboarding/setup-mfa')({
173+
component: SetupMfaPage,
154174
})
155175

156-
export const config = {
157-
matcher: [
158-
// Skip Next.js internals and all static files, unless found in search params
159-
'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
160-
// Always run for API routes
161-
'/(api|trpc)(.*)',
162-
],
176+
function SetupMfaPage() {
177+
return <TaskSetupMfa redirectUrlComplete="/dashboard" />
163178
}
164179
```
165180
</Tab>
166181
</Tabs>
167182

168-
## Build your MFA setup page
169-
170-
Follow the [custom flow guide](/docs/guides/development/custom-flows/account-updates/manage-totp-based-mfa) to build the `/account/manage-mfa/add` page.
183+
For more information about the `<TaskSetupMfa />` component, see the [component reference](/docs/reference/components/authentication/task-setup-mfa).
171184
</Steps>
185+
186+
## Additional considerations
187+
188+
- **Existing users**: Users who already have accounts will be prompted to set up MFA on their next sign-in.
189+
- **Custom flows**: If Clerk's prebuilt components don't meet your needs, you can build a custom MFA setup flow using the Clerk API. See the [custom flow guides](/docs/guides/development/custom-flows/account-updates/manage-totp-based-mfa) for more information.
190+
{/* TODO: Update link to setup-mfa custom flow guide once created */}
191+
- **Session handling**: Users with pending `setup-mfa` tasks are treated as signed-out by default. Learn more about [session tasks](/docs/guides/configure/session-tasks).

docs/manifest.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3378,6 +3378,10 @@
33783378
"title": "`<TaskResetPassword />`",
33793379
"href": "/docs/reference/components/authentication/task-reset-password"
33803380
},
3381+
{
3382+
"title": "`<TaskSetupMfa />`",
3383+
"href": "/docs/reference/components/authentication/task-setup-mfa"
3384+
},
33813385
{
33823386
"title": "`<Waitlist />`",
33833387
"href": "/docs/reference/components/authentication/waitlist"

0 commit comments

Comments
 (0)