MFA
The module supports email-based Multi-Factor Authentication as a secondary verification layer. MFA is not something you enable in the gateway: the IAM service decides when a session requires additional proof based on anomaly detection. The gateway detects the 202 response, surfaces it to your frontend, and provides the handlers and composables needed to complete the verification.
Two paradigms are available:
- Built-in MFA: triggered automatically by the IAM service during token rotation when it detects anomalies such as a new device, idle session, IP range change, or elevated risk score. The user receives an email with a magic link and a 7-digit code. The gateway ships ready-to-use routes that verify the link and code without any custom server code.
- Custom MFA: initiated by your application code for sensitive actions like password changes, email updates, or account deletion. You call
askForMfaFlowwith a reason and a cryptographic buffer, the IAM service sends the verification email, and you wrap your action handler withdefineMfaCodeVerifierHandlerordefineVerifiedMagicLinkGetHandler.
How MFA is triggered
During token rotation, the gateway calls the IAM /auth/user/refresh-session endpoint. When the IAM service runs its anomaly detection checks and determines the session needs verification, it returns HTTP 202 with { mfa: true, message: "..." } instead of rotating the tokens.
The gateway propagates this response through three layers:
| Detection point | What happens |
|---|---|
ensureValidCredentials | Returns { text: 'MFA required', message } with status 202 |
getCachedUserData | Returns { type: 'ERROR', reason: 'MFA', status: 202 } |
defineAuthenticatedEventHandler | Returns { mfaRequired: 'MFA required' } to the client |
On the client side, you should implement a method to detect that 202 status or { mfaRequired: 'MFA required' } response, and render a proper ui for the user.
If you use the Nuxt module useAuthData handles that for you, and sets mfaRequired: true on the reactive auth state. Your frontend should checks this field and redirects to the verification page.
Verification flow
Regardless of whether MFA was triggered by the built-in flow or a custom flow, verification follows the same pattern:
- The user receives an email containing a magic link and a 7-digit numeric code.
- The magic link points to a bounce URL on the gateway, which redirects to your frontend verification page with
token,random,reason, andvisitorquery parameters. - The frontend calls
useMagicLink()or the appropriate path to validate the link parameters against the server. - The user enters the 7-digit code in your UI and submits it. The server verifies the code with the IAM service, rotates both tokens, and returns success.
Token rotation on success
Successful MFA verification always triggers a full token rotation. The IAM service:
- Marks the old refresh token as consumed
- Generates a new access token and refresh token pair
- Updates
users.last_mfa_atto record when MFA was last completed - Returns the new tokens in the response
The gateway applies the rotation result by setting the __Secure-a, a-iat, and session cookies on the response. This invalidates the pre-MFA session and ensures subsequent requests use fresh credentials.
Session binding
MFA verification is bound to the existing session through three cookies:
| Cookie | Role |
|---|---|
canary_id | Ties the verification to the device fingerprint tracked by the bot detector |
session | Identifies the refresh token that initiated the flow |
__Secure-a | Provides the access token for the signed request to the IAM service |
If any of these cookies are missing, the gateway rejects the verification request with HTTP 401.
Rate limiting
The IAM service applies rate limits at multiple levels during MFA flows: per IP, per user ID, per session, and per JTI. When the limit is exceeded, the gateway receives HTTP 429 and forwards the Retry-After header to the client. See IAM Rate Limiting for the specific limits.
Built-in flows
The module ships ready-to-use server routes for the three standard verification flows. Each flow registers its own GET and POST handlers through magicLinksRouter:
| Flow | Trigger | Pages |
|---|---|---|
| Built-in MFA | Anomaly detection during token rotation | Verify magic link, submit code |
| Password Reset | User requests password reset | Initiate, verify link, submit new password |
| Email Change | User requests email change | Initiate, verify link, submit new email |
Custom flows
For sensitive actions beyond the built-in flows, you create your own endpoints using the MFA utilities:
| Utility | Purpose |
|---|---|
askForMfaFlow | Sends verification email for a custom reason |
defineVerifiedMagicLinkGetHandler | Wraps a GET handler with magic link verification |
defineMfaCodeVerifierHandler | Wraps a POST handler with code verification and token rotation |
See Custom MFA Flow for the full implementation guide.
Client-side integration
The useMagicLink composable handles the frontend verification page. It parses the URL query parameters, routes to the correct server endpoint based on the reason field, and returns the verified data for your UI to render the appropriate form. See Client-Side MFA for usage details.