114 lines
3.6 KiB
TypeScript
114 lines
3.6 KiB
TypeScript
import { sessionManager } from '~/server/utils/session-manager'
|
|
|
|
export default defineNuxtRouteMiddleware(async (to) => {
|
|
// Skip auth for SSR
|
|
if (import.meta.server) return;
|
|
|
|
// Check if auth is required (default true unless explicitly set to false)
|
|
const isAuthRequired = to.meta.auth !== false;
|
|
|
|
if (!isAuthRequired) {
|
|
console.log('[MIDDLEWARE] Auth not required for route:', to.path);
|
|
return;
|
|
}
|
|
|
|
// Skip auth check if we're already on the login page to prevent redirect loops
|
|
if (to.path === '/login' || to.path.startsWith('/auth')) {
|
|
return;
|
|
}
|
|
|
|
console.log('[MIDDLEWARE] Checking authentication for route:', to.path);
|
|
|
|
// Use session manager for centralized session handling
|
|
const nuxtApp = useNuxtApp();
|
|
const cacheKey = 'auth:session:cache';
|
|
const baseExpiry = 3 * 60 * 1000; // 3 minutes base cache
|
|
const jitter = Math.floor(Math.random() * 10000); // 0-10 seconds jitter
|
|
const cacheExpiry = baseExpiry + jitter; // Prevent thundering herd
|
|
|
|
try {
|
|
// Use SessionManager for deduped session checks
|
|
const sessionData = await sessionManager.checkSession({
|
|
nuxtApp,
|
|
cacheKey,
|
|
cacheExpiry,
|
|
fetchFn: async () => {
|
|
const controller = new AbortController();
|
|
const timeout = setTimeout(() => controller.abort(), 10000); // 10 second timeout
|
|
|
|
try {
|
|
const result = await $fetch('/api/auth/session', {
|
|
signal: controller.signal,
|
|
retry: 2,
|
|
retryDelay: 1000,
|
|
onRetry: ({ retries }: { retries: number }) => {
|
|
console.log(`[MIDDLEWARE] Retrying auth check (attempt ${retries + 1})`)
|
|
},
|
|
onResponseError({ response }) {
|
|
// Clear cache on auth errors
|
|
if (response.status === 401 || response.status === 403) {
|
|
console.log('[MIDDLEWARE] Auth error detected, clearing cache')
|
|
sessionManager.clearCache();
|
|
delete nuxtApp.payload.data?.authState;
|
|
}
|
|
}
|
|
}) as any;
|
|
|
|
clearTimeout(timeout);
|
|
return result;
|
|
} catch (error) {
|
|
clearTimeout(timeout);
|
|
throw error;
|
|
}
|
|
}
|
|
});
|
|
|
|
// Store auth state for components
|
|
if (!nuxtApp.payload.data) {
|
|
nuxtApp.payload.data = {};
|
|
}
|
|
|
|
nuxtApp.payload.data.authState = {
|
|
user: sessionData.user,
|
|
authenticated: sessionData.authenticated,
|
|
groups: sessionData.groups || []
|
|
};
|
|
|
|
console.log('[MIDDLEWARE] Session check result:', {
|
|
authenticated: sessionData.authenticated,
|
|
hasUser: !!sessionData.user,
|
|
userId: sessionData.user?.id,
|
|
groups: sessionData.groups || [],
|
|
fromCache: sessionData.fromCache,
|
|
reason: sessionData.reason
|
|
});
|
|
|
|
if (sessionData.authenticated && sessionData.user) {
|
|
console.log('[MIDDLEWARE] User authenticated, allowing access');
|
|
|
|
// Check for any auth errors from authorization middleware
|
|
if (nuxtApp.payload.authError) {
|
|
const toast = useToast();
|
|
toast.error(String(nuxtApp.payload.authError));
|
|
delete nuxtApp.payload.authError;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
console.log('[MIDDLEWARE] No valid authentication found, redirecting to login');
|
|
return navigateTo('/login');
|
|
|
|
} catch (error: any) {
|
|
console.error('[MIDDLEWARE] Auth check failed:', error);
|
|
|
|
// Show warning for cached results due to network errors
|
|
if (error.reason === 'NETWORK_ERROR_CACHED') {
|
|
const toast = useToast();
|
|
toast.warning('Network connectivity issue - using cached authentication');
|
|
}
|
|
|
|
return navigateTo('/login');
|
|
}
|
|
});
|