import type { NavigationGuard, RouteLocationNormalized, RouteLocationRaw } from 'vue-router' import { buildLoginQuery } from './redirect' import { useAuth, getRedirectConfig } from './useAuth' /** * Auth router integration for Vue Router */ // Track if auth has been initialized (for performance) let authInitialized = false /** * Reset auth initialization state * Useful for testing or app reload scenarios */ export function resetAuthState() { authInitialized = false } /** * Auth guard for Vue Router * * Protects routes requiring authentication and handles redirect logic. * Reads configuration from the auth instance created via createAuth(). * * @example * ```ts * const auth = createAuth({ * baseURL: 'https://api.example.com', * redirect: { ... } * }) * router.beforeEach(authGuard()) * ``` */ export function authGuard() { return async function guard( to: RouteLocationNormalized, _from: RouteLocationNormalized, ): Promise { const auth = useAuth() const config = getRedirectConfig() // Check if route requires authentication const requiresAuth = to.meta[config.authMetaKey] // Check if route is an auth-only page (login, signup, etc) const requiresNoAuth = config.noAuthRoutes.includes(to.name as string) try { // Only check auth once on first navigation for performance if (!authInitialized) { await auth.checkAuth() authInitialized = true } // Use cached auth state from reactive user ref const isAuthenticated = !!auth.user.value // Redirect authenticated users away from auth pages if (isAuthenticated && requiresNoAuth) { return config.authenticatedRedirect } // Redirect unauthenticated users to login for protected pages if (!isAuthenticated && requiresAuth) { const query = buildLoginQuery(to.fullPath, config) return { name: config.loginRoute, query, } } // Allow navigation return true } catch (error) { console.error('[Auth Guard] Error:', error) // On error, allow navigation but log the issue return true } } } /** * Compose multiple navigation guards into one * Guards are executed in order, stopping at the first one that returns a redirect * * @example * ```ts * router.beforeEach(composeGuards([ * authGuard(), * orgAccessGuard(), * featureFlagGuard(), * ])) * ``` */ export function composeGuards(guards: NavigationGuard[]): NavigationGuard { return async (to, from) => { for (const guard of guards) { const result = await (guard as (to: RouteLocationNormalized, from: RouteLocationNormalized) => Promise)(to, from) if (result !== undefined && result !== true) { // Guard blocked or redirected, stop here return result } } // All guards passed, allow navigation return true } }