export interface UserWithGroups { id: string; email: string; username: string; name: string; authMethod: string; groups: string[]; } export interface AuthState { user: UserWithGroups | null; authenticated: boolean; groups: string[]; } /** * Authorization composable for role-based access control */ export const useAuthorization = () => { // Simple reactive state that starts with safe defaults const authState = reactive({ user: null, authenticated: false, groups: [] }); // Loading state const isLoading = ref(true); const hasInitialized = ref(false); // Initialize auth state with comprehensive error handling const initializeAuth = async () => { if (hasInitialized.value) return; try { console.log('[useAuthorization] Initializing auth state...'); // Try to get auth from session API directly const sessionData = await $fetch('/api/auth/session') as AuthState; if (sessionData && typeof sessionData === 'object') { // Update reactive state authState.user = sessionData.user || null; authState.authenticated = sessionData.authenticated || false; authState.groups = Array.isArray(sessionData.groups) ? sessionData.groups : []; console.log('[useAuthorization] Auth state initialized:', { authenticated: authState.authenticated, groups: authState.groups, user: authState.user?.email }); hasInitialized.value = true; isLoading.value = false; return true; } } catch (error) { console.error('[useAuthorization] Failed to initialize auth:', error); } // Set safe defaults on error authState.user = null; authState.authenticated = false; authState.groups = []; hasInitialized.value = true; isLoading.value = false; return false; }; // Initialize immediately on client if (process.client) { initializeAuth(); } /** * Get current user groups from session */ const getUserGroups = (): string[] => { try { return Array.isArray(authState.groups) ? authState.groups : []; } catch (error) { console.error('[useAuthorization] Error getting user groups:', error); return []; } }; /** * Get current authenticated user */ const getCurrentUser = (): UserWithGroups | null => { try { return authState.user || null; } catch (error) { console.error('[useAuthorization] Error getting current user:', error); return null; } }; /** * Check if user has specific role/group */ const hasRole = (role: string): boolean => { const groups = getUserGroups(); return groups.includes(role); }; /** * Check if user has any of the specified roles */ const hasAnyRole = (roles: string[]): boolean => { const groups = getUserGroups(); return roles.some(role => groups.includes(role)); }; /** * Check if user has all of the specified roles */ const hasAllRoles = (roles: string[]): boolean => { const groups = getUserGroups(); return roles.every(role => groups.includes(role)); }; /** * Check if user can access a specific resource/feature */ const canAccess = (resource: string): boolean => { const groups = getUserGroups(); // Define access rules for different resources const accessRules: Record = { 'expenses': ['sales', 'admin'], 'admin-console': ['admin'], 'audit-logs': ['admin'], 'system-logs': ['admin'], 'duplicate-management': ['admin'], 'user-management': ['admin'], 'interests': ['user', 'sales', 'admin'], 'berths': ['user', 'sales', 'admin'], 'dashboard': ['user', 'sales', 'admin'], }; const requiredRoles = accessRules[resource]; if (!requiredRoles) { // If no specific rules defined, allow for authenticated users return groups.length > 0; } return hasAnyRole(requiredRoles); }; /** * Convenience methods for common role checks */ const isAdmin = (): boolean => hasRole('admin'); const isSales = (): boolean => hasRole('sales'); const isUser = (): boolean => hasRole('user'); const isSalesOrAdmin = (): boolean => hasAnyRole(['sales', 'admin']); const isUserOrAbove = (): boolean => hasAnyRole(['user', 'sales', 'admin']); /** * Get user's highest role (for display purposes) */ const getHighestRole = (): string => { const groups = getUserGroups(); if (groups.includes('admin')) return 'admin'; if (groups.includes('sales')) return 'sales'; if (groups.includes('user')) return 'user'; return 'none'; }; /** * Get role display name */ const getRoleDisplayName = (role: string): string => { const roleNames: Record = { 'admin': 'Administrator', 'sales': 'Sales Team', 'user': 'User', 'none': 'No Access' }; return roleNames[role] || role; }; /** * Get role color for UI display */ const getRoleColor = (role: string): string => { const roleColors: Record = { 'admin': 'red', 'sales': 'purple', 'user': 'blue', 'none': 'grey' }; return roleColors[role] || 'grey'; }; /** * Check if current route requires specific roles */ const checkRouteAccess = (route: any): boolean => { if (!route.meta?.roles) { // No role requirements, allow access return true; } const requiredRoles = Array.isArray(route.meta.roles) ? route.meta.roles : [route.meta.roles]; return hasAnyRole(requiredRoles); }; /** * Get navigation items filtered by user permissions */ const getFilteredNavigation = (navigationItems: any[]): any[] => { return navigationItems.filter(item => { if (!item.roles) return true; // No role restrictions return hasAnyRole(item.roles); }); }; /** * Update auth state (called by middleware) */ const updateAuthState = (newAuthState: AuthState) => { try { const nuxtApp = useNuxtApp(); if (!nuxtApp.payload.data) { nuxtApp.payload.data = {}; } nuxtApp.payload.data.authState = newAuthState; } catch (error) { console.error('[useAuthorization] Error updating auth state:', error); } }; return { // State getters getUserGroups, getCurrentUser, // Role checking hasRole, hasAnyRole, hasAllRoles, canAccess, // Convenience methods isAdmin, isSales, isUser, isSalesOrAdmin, isUserOrAbove, // Utility methods getHighestRole, getRoleDisplayName, getRoleColor, checkRouteAccess, getFilteredNavigation, // State management updateAuthState }; };