# Authentication System Documentation ## Overview Black Canyon Tickets uses a unified authentication system built on top of Supabase Auth with server-side rendering (SSR) support. All authentication functionality is centralized in a single module (`src/lib/auth-unified.ts`) to ensure consistency and maintainability. ## Architecture ### Core Module: `auth-unified.ts` The unified auth module is the single source of truth for all authentication in the application. It provides: - 🔐 **Session-based authentication** using Supabase cookies - 🎭 **Role-based access control** (User, Admin, Super Admin) - 🏢 **Organization-based access control** - 🛡️ **Security logging and rate limiting** - 📝 **Type-safe authentication context** - 🐳 **Docker-friendly cookie handling** ### Key Features 1. **Universal Compatibility**: Works with both `Request` objects (API routes) and `AstroCookies` (Astro pages) 2. **SSR Support**: Full server-side rendering support with proper cookie handling 3. **Bearer Token Fallback**: Supports Authorization header for API clients 4. **Security Hardening**: Built-in CSRF protection, rate limiting, and security logging 5. **Type Safety**: Full TypeScript support with proper types ## Usage Guide ### Basic Authentication Check ```typescript // In Astro pages (.astro files) import { verifyAuth } from '../lib/auth-unified'; // Check if user is authenticated (returns null if not) const auth = await verifyAuth(Astro.cookies); // In API routes (.ts files) const auth = await verifyAuth(request); ``` ### Requiring Authentication ```typescript // Throws AuthError if not authenticated const auth = await requireAuth(Astro.cookies); // The auth object contains: // - user: Supabase user object // - session: Session information // - isAdmin: boolean // - isSuperAdmin: boolean // - organizationId: string | null ``` ### Role-Based Access Control ```typescript // Require admin access const auth = await requireAdmin(Astro.cookies); // Require super admin access const auth = await requireSuperAdmin(Astro.cookies); // Check organization access const auth = await requireOrganizationAccess(Astro.cookies, organizationId); ``` ### Protected Page Pattern ```typescript --- import Layout from '../layouts/Layout.astro'; import { verifyAuth } from '../lib/auth-unified'; export const prerender = false; // Enable SSR const auth = await verifyAuth(Astro.cookies); if (!auth) { return Astro.redirect('/login'); } ---

Welcome, {auth.user.email}!

``` ### Protected API Route Pattern ```typescript import type { APIRoute } from 'astro'; import { requireAuth, createAuthResponse } from '../lib/auth-unified'; export const GET: APIRoute = async ({ request }) => { try { const auth = await requireAuth(request); // Your protected logic here const data = { userId: auth.user.id }; return createAuthResponse(data); } catch (error) { return createAuthResponse( { error: error.message }, error.statusCode || 401 ); } }; ``` ### Using the Middleware Pattern ```typescript import { withAuth } from '../lib/auth-unified'; // Wrap your handler with authentication export const GET: APIRoute = async ({ request }) => { return withAuth(request, async (auth) => { // This code only runs if authenticated return new Response(JSON.stringify({ message: `Hello ${auth.user.email}` })); }); }; ``` ## Authentication Flow 1. **Login**: User submits credentials → `/api/auth/login` → Supabase sets cookies → Redirect to dashboard 2. **Session Check**: Every request → `verifyAuth()` checks cookies → Returns auth context or null 3. **Protected Routes**: Page/API checks auth → Redirects to login or returns 401 if not authenticated 4. **Logout**: Clear session → `/api/auth/logout` → Supabase clears cookies → Redirect to login ## Error Handling The auth system uses a custom `AuthError` class with specific error codes: ```typescript try { const auth = await requireAuth(request); } catch (error) { if (error instanceof AuthError) { switch (error.code) { case 'NO_SESSION': // User not logged in break; case 'NO_PERMISSION': // User lacks required role break; case 'EXPIRED': // Session expired break; } } } ``` ## Security Features ### CSRF Protection ```typescript // Generate token for forms const csrfToken = generateCSRFToken(); // Verify token on submission if (!verifyCSRFToken(request, sessionToken)) { throw new Error('Invalid CSRF token'); } ``` ### Rate Limiting ```typescript // Check rate limit (10 requests per minute by default) const identifier = `login:${email}`; if (!checkRateLimit(identifier, 5, 60000)) { throw new Error('Too many attempts'); } ``` ### Security Logging All authentication events are automatically logged: - Failed login attempts - Successful authentications - Permission denied events - Rate limit violations ## Testing Visit `/auth-test-unified` to test the authentication system. This page shows: - Current authentication status - Session information - Request headers and cookies - Links to test protected routes ## Migration Guide ### From Old Auth System 1. **Update imports**: ```typescript // Old import { verifyAuth } from '../lib/auth'; // New (but old path still works via proxy) import { verifyAuth } from '../lib/auth-unified'; ``` 2. **Use Astro.cookies instead of Astro.request**: ```typescript // Old const auth = await verifyAuth(Astro.request); // New (better SSR support) const auth = await verifyAuth(Astro.cookies); ``` 3. **Handle new error types**: ```typescript // Old if (!auth) throw new Error('Not authenticated'); // New import { AuthError } from '../lib/auth-unified'; if (!auth) throw new AuthError('Not authenticated', 'NO_SESSION'); ``` ## Docker Considerations The unified auth system is designed to work seamlessly in Docker environments: 1. **Cookie Domain**: Automatically handles cookie domain settings 2. **Secure Cookies**: Uses secure cookies in production 3. **Proxy Support**: Correctly reads IP addresses behind proxies ## Environment Variables ```bash # Required for auth to work PUBLIC_SUPABASE_URL=https://your-project.supabase.co PUBLIC_SUPABASE_ANON_KEY=your-anon-key SUPABASE_SERVICE_ROLE_KEY=your-service-key # Optional for enhanced security COOKIE_DOMAIN=.yourdomain.com # For subdomain support NODE_ENV=production # Enables secure cookies ``` ## Troubleshooting ### Common Issues 1. **"No session found" after login** - Check cookie settings in browser - Verify Supabase URL and keys are correct - Ensure cookies are not blocked 2. **Dashboard flashing before redirect** - Ensure using `Astro.cookies` not `Astro.request` - Verify `prerender = false` is set - Check server-side auth is enabled 3. **Auth works locally but not in Docker** - Check `COOKIE_DOMAIN` environment variable - Verify proxy headers are being forwarded - Ensure secure cookie settings match environment ### Debug Mode Enable debug logging in development: ```typescript // In your page or API route import { authDebug } from '../lib/auth-unified'; authDebug.logCookies(request); authDebug.logSession(session); ``` ## Best Practices 1. **Always use the unified auth module** - Don't create separate auth implementations 2. **Use `Astro.cookies` for pages** - Better SSR support than `Astro.request` 3. **Handle errors gracefully** - Show user-friendly messages, not technical errors 4. **Test auth flows regularly** - Use `/auth-test-unified` to verify functionality 5. **Keep sessions secure** - Use HTTPS in production, set proper cookie flags ## Future Enhancements - [ ] Refresh token rotation - [ ] Remember me functionality - [ ] Two-factor authentication - [ ] Session activity tracking - [ ] IP-based session validation