Middleware

two-factor

Route middleware to protect 2FA-enabled routes

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:

  1. Extracts the 2FA token from cookies
  2. Validates the 2FA token hasn't expired
  3. If validation fails: Logs out and redirects to login

Client-Side Validation

On the client, the middleware:

  1. Extracts the 2FA token from localStorage
  2. Validates the 2FA token hasn't expired
  3. Checks strategy consistency
  4. Validates user auth state
  5. 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:

  1. Logs out the current session
  2. Redirects to the configured login path
  3. 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-factor middleware 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:auth middleware