Security Features
Learn about security features in Nuxt Umbu, including CSRF protection and two-factor authentication (2FA) implementation.
Overview
Nuxt Umbu provides built-in security features to protect your authentication system:
- CSRF Protection: Cross-Site Request Forgery protection for Sanctum
- Two-Factor Authentication: Optional 2FA support for enhanced security
- Secure Cookie Configuration: Production-ready cookie security settings
CSRF Protection
What is CSRF?
Cross-Site Request Forgery (CSRF) is an attack that forces an end user to execute unwanted actions on a web application in which they're currently authenticated.
Sanctum CSRF
Nuxt Umbu integrates with Laravel Sanctum's CSRF protection:
// nuxt.config.ts
export default defineNuxtConfig({
auth: {
provider: 'sanctum',
csrf: '/sanctum/csrf-cookie' // CSRF cookie endpoint
}
})
How CSRF Works
- Initial Request: Client requests CSRF cookie
- Cookie Set: Server sets CSRF cookie
- Subsequent Requests: Client includes CSRF token in headers
- Validation: Server validates token before processing
Using CSRF Protection
Automatic CSRF Handling
Nuxt Umbu automatically handles CSRF for Sanctum:
import { ensureCsrf } from '#auth-utils'
// Ensure CSRF token is present before authenticated requests
await ensureCsrf()
// Now make authenticated requests
const response = await $fetch('/api/protected', {
method: 'POST',
body: { data: 'value' }
})
Manual CSRF Token
If you need the CSRF token manually:
import { useAuthStore } from '#auth-utils'
const authStore = useAuthStore()
const csrfToken = authStore.csrfToken
// Use in custom requests
await $fetch('/api/protected', {
headers: {
'X-XSRF-TOKEN': csrfToken
}
})
CSRF Configuration
Custom CSRF Endpoint
auth: {
provider: 'sanctum',
csrf: '/api/csrf-token' // Custom endpoint
}
CSRF in Development
CSRF is automatically handled in development with relaxed security:
if (isDev) {
options.cookie.prefix = 'auth.'
options.cookie.options.secure = false
}
Passport CSRF (Optional)
Passport provider can optionally use CSRF:
auth: {
provider: 'passport',
csrf: '/api/csrf-token' // Optional CSRF endpoint
}
Two-Factor Authentication (2FA)
What is 2FA?
Two-Factor Authentication adds an extra layer of security by requiring a second form of verification in addition to your password.
Enabling 2FA
2FA is automatically enabled when you configure a twoFactor endpoint:
strategies: {
password: {
endpoints: {
login: { url: '/oauth/token', method: 'post' },
user: { url: '/api/user', method: 'get' },
twoFactor: {
url: '/api/2fa/verify',
method: 'post',
property: 'access_token',
expires: 'expires_in',
headerName: 'X-2FA-Token'
}
},
redirect: {
login: '/login',
logout: '/',
twoFactor: '/2fa'
}
}
}
2FA Configuration Options
TwoFactorFetchOption
type TwoFactorFetchOption = {
url: string // 2FA verification endpoint
method: string // HTTP method
property?: string // Where token comes from (default: access_token)
expires?: string // Where expiration comes from (default: expires_in)
headerName?: string // Header name for token (default: '2fa')
}
Configuration Example
twoFactor: {
url: '/api/2fa/verify',
method: 'post',
property: 'token', // Response property containing token
expires: 'expires', // Response property containing expiration
headerName: 'Authorization' // Custom header name
}
Using 2FA
Middleware Protection
When 2FA is enabled, the middleware automatically protects routes:
// nuxt.config.ts
export default defineNuxtConfig({
auth: {
// 2FA endpoint configured
strategies: {
password: {
endpoints: {
twoFactor: { url: '/api/2fa/verify', method: 'post' }
}
}
}
}
})
// Middleware is automatically registered
// Routes with twoFactor middleware will require 2FA verification
Manual 2FA Verification
const { $auth } = useNuxtApp()
// Verify 2FA code
const result = await $auth.twoFactor('password', '123456')
if (result.success) {
// 2FA verified, user authenticated
navigateTo('/dashboard')
} else {
// Invalid code
showError('Invalid 2FA code')
}
2FA in Components
<script setup lang="ts">
const { $auth } = useNuxtApp()
const code = ref('')
const verify2FA = async () => {
const result = await $auth.twoFactor('password', code.value)
if (result.success) {
await navigateTo('/dashboard')
} else {
alert('Invalid code')
}
}
</script>
<template>
<form @submit.prevent="verify2FA">
<input v-model="code" type="text" placeholder="Enter 2FA code" />
<button type="submit">Verify</button>
</form>
</template>
2FA Redirects
Configure where users are redirected during 2FA flow:
redirect: {
login: '/login',
logout: '/',
twoFactor: '/2fa', // Redirect to 2FA page when needed
home: '/dashboard'
}
2FA Middleware
The 2FA middleware is automatically registered when you configure a twoFactor endpoint:
// Automatically registered
addRouteMiddleware({
name: 'umbu:two-factor',
path: resolve('./runtime/' + provider + '/middleware/twoFactor')
})
Using 2FA Middleware
<script setup>
// Protect route with 2FA middleware
definePageMeta({
middleware: ['umbu:two-factor']
})
</script>
Security Best Practices
CSRF Protection
- Always enable CSRF in production
auth: { csrf: '/sanctum/csrf-cookie' } - Use HTTPS in production
cookie: { options: { secure: true } } - Validate CSRF on server
- Ensure your backend validates CSRF tokens
- Use Sanctum's built-in CSRF protection
2FA Implementation
- Use secure 2FA methods
- TOTP (Time-based One-Time Password)
- SMS codes (with rate limiting)
- Hardware tokens
- Implement rate limiting
- Limit 2FA attempts
- Lock accounts after failed attempts
- Backup codes
- Provide backup recovery codes
- Allow secure code regeneration
- Session management
- Remember 2FA for trusted devices
- Require re-verification for sensitive actions
Cookie Security
- Use secure cookie settings in production
cookie: { prefix: '__Secure-', options: { httpOnly: true, secure: true, sameSite: 'Strict' } } - Set appropriate expiration
options: { maxAge: 60 * 60 * 24 * 7 // 7 days } - Use proper prefixes
__Secure-for production__Host-for strict security
Common Security Issues
CSRF Token Missing
Problem: CSRF token not included in requests
Solution:
import { ensureCsrf } from '#auth-utils'
// Ensure CSRF token before requests
await ensureCsrf()
2FA Not Working
Problem: 2FA verification fails
Solution:
- Verify endpoint configuration
- Check response property names
- Ensure header name matches backend expectations
- Verify code format (6 digits, etc.)
Cookie Security Issues
Problem: Cookies not secure in production
Solution:
cookie: {
prefix: '__Secure-',
options: {
secure: true,
httpOnly: true,
sameSite: 'Strict'
}
}
Testing Security Features
Testing CSRF
// test/csrf.test.ts
import { describe, it, expect } from 'vitest'
import { ensureCsrf } from '#auth-utils'
describe('CSRF Protection', () => {
it('should fetch CSRF token', async () => {
await ensureCsrf()
// Verify token is present
})
})
Testing 2FA
// test/2fa.test.ts
import { describe, it, expect } from 'vitest'
describe('2FA', () => {
it('should verify 2FA code', async () => {
const { $auth } = useNuxtApp()
const result = await $auth.twoFactor('password', '123456')
expect(result.success).toBe(true)
})
})
Security Checklist
Before Production
- CSRF protection enabled
- HTTPS configured
- Secure cookie settings
- 2FA implemented for sensitive actions
- Rate limiting configured
- Session management configured
- Error handling doesn't leak information
- Logging configured for security events
Regular Security Audits
- Review dependency updates
- Check for security advisories
- Audit authentication flows
- Test for common vulnerabilities
- Review access logs
- Update security configurations
Troubleshooting
CSRF Issues
CSRF cookie not being set
- Verify CSRF endpoint is correct
- Check browser console for errors
- Ensure cookies are enabled
- Verify backend CSRF configuration
CSRF token validation failing
- Check token format
- Verify header name
- Ensure token is included in requests
- Check backend validation logic
2FA Issues
2FA middleware not triggering
- Verify twoFactor endpoint is configured
- Check that middleware is registered
- Ensure route has middleware applied
- Check redirect configuration
2FA verification failing
- Verify endpoint URL and method
- Check response property names
- Ensure code format is correct
- Verify header name matches backend