7.1 KiB
404 and Session Expiration Fixes
Issues Addressed
- 404 Error on Expenses Page - The expenses page was returning a 404 error
- Session Expiration After 404 - Users were getting logged out after encountering the 404 error
- Immediate Session Expiration - Users were getting logged out immediately after logging in
- External Service 401 Errors - 401 errors from external services (CMS, database) were logging users out
Root Cause Analysis
404 Error Cause
- The expenses page was missing the authorization middleware configuration
- The dashboard layout referenced in the page metadata didn't exist
- Nuxt wasn't properly configured to use layouts
Session Expiration Cause
- The authentication middleware was incorrectly clearing the session cache on ALL errors (including 404s)
- This caused a valid session to be invalidated when encountering any page error
Immediate Logout Cause
- The authorization middleware was making its own API call, bypassing the session cache
- The auth refresh plugin's 2-minute periodic validation was conflicting with the 3-minute session cache
- Multiple concurrent session checks were causing race conditions
External Service 401 Cause
- The auth error handler was treating ANY 401 error as a session expiration
- When the CMS (
cms.portnimara.dev) returned 401, it triggered a logout - The handler didn't distinguish between auth API errors and external service errors
Fixes Implemented
1. Fixed Expenses Page Metadata
File: pages/dashboard/expenses.vue
Added proper middleware configuration:
definePageMeta({
middleware: ['authentication', 'authorization'],
layout: 'dashboard',
roles: ['sales', 'admin']
});
This ensures:
- Authentication is checked first
- Authorization checks for sales/admin roles
- Proper layout is applied
2. Fixed Authentication Middleware
File: middleware/authentication.ts
Updated error handling to only clear cache on actual auth errors:
onResponseError({ response }) {
// Clear cache only on actual auth errors, not 404s or other errors
if (response.status === 401) {
console.log('[MIDDLEWARE] Unauthorized error detected, clearing cache')
sessionManager.clearCache();
delete nuxtApp.payload.data?.authState;
} else if (response.status === 403) {
console.log('[MIDDLEWARE] Forbidden error detected, partial cache clear')
// Don't clear cache on 403 as user is authenticated but lacks permissions
}
// Ignore 404s and other errors - they're not authentication issues
}
3. Enabled Layout Support
File: app.vue
Updated to support layouts:
<template>
<NuxtPwaManifest />
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
<GlobalToast />
</template>
4. Created Dashboard Layout
File: layouts/dashboard.vue
Created a full dashboard layout with:
- Navigation drawer with role-based menu items
- App bar showing user info and role badges
- Proper logout functionality
- Responsive design with rail mode
- Safe auth state access to prevent initialization errors
5. Fixed Authorization Middleware
File: middleware/authorization.ts
Updated to use cached auth state instead of making API calls:
// Get auth state from authentication middleware (already cached)
const nuxtApp = useNuxtApp();
const authState = nuxtApp.payload?.data?.authState;
This prevents:
- Duplicate API calls
- Race conditions between middlewares
- Session cache conflicts
6. Adjusted Auth Refresh Plugin
File: plugins/01.auth-refresh.client.ts
- Changed periodic validation from 2 to 5 minutes to avoid conflicts with 3-minute cache
- Added failure counting - only logs out after 3 consecutive failures
- Increased random offset to prevent thundering herd
7. Fixed Auth Error Handler
File: plugins/02.auth-error-handler.client.ts
Updated to only handle 401/403 errors from application endpoints:
// Only handle authentication errors from our own API endpoints
const isAuthEndpoint = response.url && (
response.url.includes('/api/auth/') ||
response.url.includes('/api/') && !response.url.includes('cms.portnimara.dev') && !response.url.includes('database.portnimara.com')
)
// Handle authentication errors (401, 403) only from our API
if ((response.status === 401 || response.status === 403) && isAuthEndpoint) {
// Clear auth and redirect
} else if (response.status === 401 && !isAuthEndpoint) {
console.log('[AUTH_ERROR_HANDLER] Ignoring 401 from external service:', response.url)
// Don't clear auth for external service 401s
}
This prevents external service authentication errors from logging users out.
Expected Results
- Expenses page should now load properly for users with sales or admin roles
- 404 errors won't cause session expiration - only actual authentication failures (401) will clear the session
- Better error handling - 403 errors (insufficient permissions) will redirect to dashboard with a message instead of logging out
- Consistent layout across all dashboard pages
- No immediate logout - Session checks are properly coordinated and cached
- Stable session management - No conflicts between different auth checking mechanisms
- External service errors ignored - 401 errors from CMS or database won't log users out
Testing Steps
- Log in with a user that has sales or admin role
- Navigate to
/dashboard/expenses - Verify the page loads without 404
- If you don't have the required role, you should be redirected to dashboard with an error message (not logged out)
- Try navigating to a non-existent page - you should get a 404 but remain logged in
Additional Improvements
- The authorization middleware now stores error messages that are displayed via toast
- The authorization middleware uses cached auth state instead of making API calls
- The dashboard layout shows the current user and their role with safe access patterns
- Navigation menu dynamically shows/hides items based on user roles
- Session validation continues to work with the 3-minute cache + jitter to prevent race conditions
- Auth refresh plugin runs validation every 5 minutes to avoid cache conflicts
- Multiple failure tolerance prevents transient issues from logging users out
- Auth error handler differentiates between app and external service errors
Timing Configuration Summary
- Session Cache: 3 minutes (with 0-10 second jitter)
- Auth Refresh Validation: Every 5 minutes (with 0-10 second offset)
- Token Refresh: 5 minutes before token expiry
- Failure Tolerance: 3 consecutive failures before logout
This configuration ensures no timing conflicts between different auth mechanisms.
External Service Integration
The auth error handler now properly handles errors from external services:
- CMS errors (cms.portnimara.dev) - 401 errors are logged but don't trigger logout
- Database errors (database.portnimara.com) - 401 errors are logged but don't trigger logout
- App API errors (/api/*) - 401/403 errors still trigger logout as expected
This allows the app to gracefully handle authentication failures with integrated services without disrupting the user's main session.