/** * Route Protection Hooks * Professional Next.js Resume Builder - Enterprise Auth System * * @author David Valera Melendez * @created 2025-08-08 * @location Made in Germany 🇩🇪 */ 'use client'; import React, { useEffect } from 'react'; import { useRouter, usePathname } from 'next/navigation'; import { useAuthStore } from '@/lib/auth-store'; import { UserRole } from '@/types/auth'; /** * Hook to protect routes that require authentication */ export function useAuthGuard(options?: { requiredRoles?: UserRole[]; redirectTo?: string; checkOnMount?: boolean; }) { const router = useRouter(); const pathname = usePathname(); const { isAuthenticated, user, checkAuth } = useAuthStore(); const { requiredRoles = [], redirectTo = '/auth/login', checkOnMount = true, } = options || {}; useEffect(() => { if (checkOnMount) { checkAuth(); } }, [checkAuth, checkOnMount]); useEffect(() => { // Skip check during initial load if (checkOnMount && !isAuthenticated && !user) { return; } // Check authentication if (!isAuthenticated) { const loginUrl = new URL(redirectTo, window.location.origin); loginUrl.searchParams.set('returnUrl', pathname); router.push(loginUrl.toString() as any); return; } // Check role requirements if (requiredRoles.length > 0 && user) { const hasRequiredRole = requiredRoles.some(role => user.roles.includes(role) ); if (!hasRequiredRole) { router.push('/access-denied'); return; } } }, [isAuthenticated, user, router, pathname, requiredRoles, redirectTo]); return { isAuthenticated, user, hasRequiredRole: (role: UserRole) => user?.roles.includes(role) || false, hasAnyRole: (roles: UserRole[]) => roles.some(role => user?.roles.includes(role)) || false, }; } /** * Hook to protect routes that should only be accessible to guests (non-authenticated users) */ export function useGuestGuard(redirectTo: string = '/dashboard') { const router = useRouter(); const { isAuthenticated, checkAuth } = useAuthStore(); useEffect(() => { checkAuth(); }, [checkAuth]); useEffect(() => { if (isAuthenticated) { router.push(redirectTo as any); } }, [isAuthenticated, router, redirectTo]); return { isAuthenticated }; } /** * Hook to check if user has specific permissions */ export function usePermissions() { const { user } = useAuthStore(); const hasPermission = (permission: string): boolean => { return user?.permissions?.includes(permission) || false; }; const hasRole = (role: UserRole): boolean => { return user?.roles?.includes(role) || false; }; const hasAnyRole = (roles: UserRole[]): boolean => { return roles.some(role => hasRole(role)); }; const hasAllRoles = (roles: UserRole[]): boolean => { return roles.every(role => hasRole(role)); }; const isAdmin = (): boolean => { return hasRole(UserRole.ADMIN); }; const isModerator = (): boolean => { return hasRole(UserRole.MODERATOR); }; const canAccess = (resource: string, action: string): boolean => { const permission = `${resource}:${action}`; return hasPermission(permission) || isAdmin(); }; return { user, hasPermission, hasRole, hasAnyRole, hasAllRoles, isAdmin, isModerator, canAccess, }; } /** * Hook for handling authentication redirects */ export function useAuthRedirect() { const router = useRouter(); const pathname = usePathname(); const redirectToLogin = (returnUrl?: string) => { const loginUrl = new URL('/auth/login', window.location.origin); loginUrl.searchParams.set('returnUrl', returnUrl || pathname); router.push(loginUrl.toString() as any); }; const redirectToDashboard = () => { router.push('/dashboard'); }; const redirectToReturnUrl = (defaultUrl: string = '/dashboard') => { const urlParams = new URLSearchParams(window.location.search); const returnUrl = urlParams.get('returnUrl'); if (returnUrl && returnUrl.startsWith('/') && !returnUrl.startsWith('//')) { router.push(returnUrl as any); } else { router.push(defaultUrl as any); } }; const redirectToAccessDenied = () => { router.push('/access-denied'); }; return { redirectToLogin, redirectToDashboard, redirectToReturnUrl, redirectToAccessDenied, }; } /** * Hook to check authentication status and handle loading states */ export function useAuthStatus() { const { isAuthenticated, isLoading, user, error, checkAuth, clearError } = useAuthStore(); useEffect(() => { if (!isAuthenticated && !isLoading) { checkAuth(); } }, [isAuthenticated, isLoading, checkAuth]); return { isAuthenticated, isLoading, user, error, clearError, }; } /** * Hook for checking specific route permissions */ export function useRoutePermissions(routePath: string) { const { user } = useAuthStore(); // Define route permissions const routePermissions: Record = { '/admin': { requiredRoles: [UserRole.ADMIN] }, '/admin/users': { requiredRoles: [UserRole.ADMIN] }, '/admin/analytics': { requiredRoles: [UserRole.ADMIN] }, '/moderation': { requiredRoles: [UserRole.ADMIN, UserRole.MODERATOR] }, '/dashboard': { requiredRoles: [UserRole.USER, UserRole.ADMIN, UserRole.MODERATOR] }, '/profile': { requiredRoles: [UserRole.USER, UserRole.ADMIN, UserRole.MODERATOR] }, '/resume-builder': { requiredRoles: [UserRole.USER, UserRole.ADMIN, UserRole.MODERATOR] }, }; const permissions = routePermissions[routePath]; if (!permissions) { return { canAccess: true, missingPermissions: [] }; } const missingPermissions: string[] = []; let canAccess = true; // Check required roles if (permissions.requiredRoles && user) { const hasRequiredRole = permissions.requiredRoles.some(role => user.roles.includes(role) ); if (!hasRequiredRole) { canAccess = false; missingPermissions.push(`Required roles: ${permissions.requiredRoles.join(', ')}`); } } // Check required permissions if (permissions.requiredPermissions && user) { const missingPerms = permissions.requiredPermissions.filter(permission => !user.permissions?.includes(permission) ); if (missingPerms.length > 0) { canAccess = false; missingPermissions.push(`Required permissions: ${missingPerms.join(', ')}`); } } return { canAccess, missingPermissions, requiredRoles: permissions.requiredRoles || [], requiredPermissions: permissions.requiredPermissions || [], }; } /** * Higher-order component for route protection */ export function withAuthGuard( Component: React.ComponentType, options?: { requiredRoles?: UserRole[]; redirectTo?: string; loading?: React.ComponentType; } ) { return function AuthGuardedComponent(props: T) { const { isAuthenticated, user } = useAuthGuard(options); if (!isAuthenticated || !user) { if (options?.loading) { const LoadingComponent = options.loading; return React.createElement(LoadingComponent); } return React.createElement('div', null, 'Loading...'); } return React.createElement(Component, props); }; } /** * Higher-order component for guest-only routes */ export function withGuestGuard( Component: React.ComponentType, redirectTo?: string ) { return function GuestGuardedComponent(props: T) { const { isAuthenticated } = useGuestGuard(redirectTo); if (isAuthenticated) { return React.createElement('div', null, 'Redirecting...'); } return React.createElement(Component, props); }; }