|
| 1 | +--- |
| 2 | +title: Force MFA for all users |
| 3 | +description: Learn how to force Multi-Factor Authentication in your Clerk application. |
| 4 | +--- |
| 5 | + |
| 6 | +<TutorialHero |
| 7 | + beforeYouStart={[ |
| 8 | + { |
| 9 | + title: "Customize your session token", |
| 10 | + link: "/docs/backend-requests/making/custom-session-token", |
| 11 | + icon: "clerk", |
| 12 | + }, |
| 13 | + { |
| 14 | + title: "Build a custom flow for managing TOTP-based multi-factor authentication", |
| 15 | + link: "/docs/custom-flows/manage-totp-based-mfa", |
| 16 | + icon: "globe", |
| 17 | + } |
| 18 | + ]} |
| 19 | +> |
| 20 | + - Detect if a user has MFA enabled or not |
| 21 | + - Redirect users to set up MFA if it is not enabled |
| 22 | +</TutorialHero> |
| 23 | + |
| 24 | +While Clerk does not natively enforce MFA for all users, you can implement this functionality by using `clerkMiddleware()` to check whether a user has MFA enabled. |
| 25 | + |
| 26 | +The following example demonstrates how to force MFA for all users. It uses `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. |
| 27 | + |
| 28 | +<Steps> |
| 29 | + ## Customize the Session Token to Include `two_factor_enabled` Property |
| 30 | + |
| 31 | + 1. In the Clerk Dashboard, navigate to the [**Sessions**](https://dashboard.clerk.com/last-active?path=sessions) page. |
| 32 | + 1. In the **Customize your session token** section, click the **Edit** button. |
| 33 | + 1. In the modal that opens, add the `two_factor_enabled` property to the session token. |
| 34 | + |
| 35 | +  |
| 36 | + |
| 37 | + ## Create a Middleware to Check if MFA is Enabled |
| 38 | + |
| 39 | + After you followed this guide [here](/docs/backend-requests/making/custom-session-token), you should have a middleware that checks if a user is logged in. You can add the following code to that middleware to check if the user has MFA enabled. |
| 40 | + |
| 41 | + ```tsx {{ filename: 'middleware.ts', collapsible: true }} |
| 42 | + import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server' |
| 43 | + import { NextResponse } from 'next/server' |
| 44 | + |
| 45 | + const isMFARoute = createRouteMatcher(['/mfa(.*)']) |
| 46 | + const isSignInRoute = createRouteMatcher(['/sign-in(.*)']) |
| 47 | + export default clerkMiddleware(async (auth, req) => { |
| 48 | + const { userId, sessionClaims } = await auth() |
| 49 | + |
| 50 | + if (userId !== null && isSignInRoute(req) && !isMFARoute(req)) { |
| 51 | + // redirect to root if logged in |
| 52 | + return NextResponse.redirect(new URL('/', req.url)) |
| 53 | + } |
| 54 | + if (userId !== null && !isMFARoute(req)) { |
| 55 | + if (sessionClaims.isMfa === false) { |
| 56 | + return NextResponse.redirect(new URL('/mfa', req.url)) |
| 57 | + } |
| 58 | + } |
| 59 | + }) |
| 60 | + |
| 61 | + export const config = { |
| 62 | + matcher: [ |
| 63 | + // Skip Next.js internals and all static files, unless found in search params |
| 64 | + '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)', |
| 65 | + // Always run for API routes |
| 66 | + '/(api|trpc)(.*)', |
| 67 | + ], |
| 68 | + } |
| 69 | + ``` |
| 70 | +</Steps> |
0 commit comments