two-factor
The two-factor middleware protects routes that require two-factor authentication (2FA). It validates that the user has completed the 2FA verification process before allowing access to protected routes.
Overview
The two-factor middleware is similar to the auth middleware but specifically validates 2FA tokens. It ensures that users who have 2FA enabled on their account have completed the second factor authentication.
Provider-Specific Behavior
Sanctum Provider
For Sanctum, the two-factor middleware:
- Validates the 2FA cookie token
- Checks token expiration
- Validates strategy consistency
- Handles server-side and client-side validation differently
Passport Provider
For Passport, the two-factor middleware:
- Validates the 2FA access token
- Checks token expiration
- Validates strategy consistency
- Handles server-side and client-side validation differently
Usage
Basic Usage
// pages/settings/security.vue
definePageMeta({
middleware: ['umbu:two-factor']
})
Combined with Auth Middleware
// pages/admin/index.vue
definePageMeta({
middleware: ['umbu:auth', 'umbu:two-factor']
})
Conditional 2FA
// pages/sensitive-operation.vue
<script setup>
definePageMeta({
middleware: ['umbu:auth', 'umbu:two-factor']
})
</script>
For conditional 2FA requirements, implement the logic within your page component or create a custom middleware that checks the user's 2FA status before applying protection.
How It Works
Server-Side Validation
On the server, the middleware:
- Extracts the 2FA token from cookies
- Validates the 2FA token hasn't expired
- If validation fails: Logs out and redirects to login
Client-Side Validation
On the client, the middleware:
- Extracts the 2FA token from localStorage
- Validates the 2FA token hasn't expired
- Checks strategy consistency
- Validates user auth state
- If validation fails: Logs out and redirects to login
Validation Logic
Sanctum Validation
// Server-side
const token = strategyName
? useCookie<string | null>($auth.prefix + `_2fa.` + strategyName).value
: null
if (!validateSessionHas2FA(strategyName, token, null, true)) {
return await handleLogout(strategyName, redirectPath, 'has2FA')
}
// Client-side
const token2FA = strategyName
? localStorage.getItem($auth.prefix + `_2fa.` + strategyName)
: null
if (!validateSessionHas2FA(strategyName, token2FA, null, false) ||
$auth.strategy !== strategyName ||
$auth.strategy !== store.value.strategy) {
return await handleLogout(strategyName, redirectPath, 'has2FA')
}
Passport Validation
// Server-side
const { strategy, token, expires } = extractServerAuthData($auth, '2fa')
if (!validateSession(strategy, token, expires)) {
return await handleLogout(strategy, redirectPath, 'has2FA')
}
// Client-side
const { strategy, token, expires } = extractClientAuthData($auth, '2fa')
if (!validateSession(strategy, token, expires) ||
!validateStrategyConsistency($auth, store, strategy)) {
return await handleLogout(strategy, redirectPath, 'has2FA')
}
Token Storage
Sanctum 2FA Token Storage
- Server: Stored in a cookie with the pattern
{prefix}_2fa.{strategy} - Client: Stored in localStorage with the pattern
{prefix}_2fa.{strategy}
Passport 2FA Token Storage
- Server: Stored in a cookie with the pattern
{prefix}_2fa.{strategy} - Client: Stored in localStorage with the pattern
{prefix}_2fa.{strategy}
Redirect Behavior
When 2FA validation fails, the middleware:
- Logs out the current session
- Redirects to the configured login path
- The user must complete both login and 2FA verification again
The redirect path is configured in your auth setup:
// nuxt.config.ts
export default defineNuxtConfig({
auth: {
redirect: {
login: '/login',
logout: '/',
home: '/dashboard',
callback: '/callback'
}
}
})
Examples
Protecting Security Settings
// pages/settings/security.vue
<script setup>
definePageMeta({
middleware: ['umbu:auth', 'umbu:two-factor']
})
</script>
<template>
<div>
<h1>Security Settings</h1>
<!-- Protected security settings -->
</div>
</template>
Protecting Sensitive Operations
// pages/payments/index.vue
<script setup>
definePageMeta({
middleware: ['umbu:auth', 'umbu:two-factor']
})
</script>
<template>
<div>
<h1>Payment Methods</h1>
<!-- Protected payment information -->
</div>
</template>
Conditional 2FA Based on User
// pages/admin/users.vue
<script setup>
definePageMeta({
middleware: ['umbu:auth', 'umbu:two-factor']
})
</script>
For user-specific 2FA requirements, implement the logic within your page component or create a custom middleware that checks the user's 2FA status before applying protection.
Error Handling
The middleware automatically handles 2FA authentication errors:
- Missing 2FA token: Logs out and redirects
- Expired 2FA token: Logs out and redirects
- Invalid 2FA token: Logs out and redirects
- Strategy mismatch: Logs out and redirects
Best Practices
When to Use Two-Factor Middleware
Use the two-factor middleware for:
- Security settings pages
- Payment information pages
- Administrative panels
- Sensitive operations (password changes, email changes)
- API endpoints that handle sensitive data
Combining with Auth Middleware
Always combine with the auth middleware:
// Good
definePageMeta({
middleware: ['umbu:auth', 'umbu:two-factor']
})
// Not recommended - 2FA alone doesn't validate basic auth
definePageMeta({
middleware: ['umbu:two-factor']
})
User Experience Considerations
- Inform users when 2FA is required
- Provide clear messaging about 2FA requirements
- Allow users to enable/disable 2FA in their settings
- Consider implementing "remember this device" for better UX
Notes
- The
umbu:two-factormiddleware is automatically registered by the module - No manual import required
- Works with both Sanctum and Passport providers
- Validation logic differs between providers
- Automatic logout on validation failure
- Should always be combined with the
umbu:authmiddleware