/** * Authentication & Authorization Test Suite * * Tests auth flows, MFA enforcement, role checks, and session management. */ import { supabase } from '@/lib/supabaseClient'; import type { TestSuite, TestResult } from '../testRunner'; export const authTestSuite: TestSuite = { id: 'auth', name: 'Authentication & Authorization', description: 'Tests for auth flows, MFA, roles, and permissions', tests: [ { id: 'auth-001', name: 'User Session Validation', description: 'Validates current user session is valid with proper JWT structure', run: async (): Promise => { const startTime = Date.now(); try { // Get current session const { data: { session }, error } = await supabase.auth.getSession(); if (error) throw new Error(`Session fetch failed: ${error.message}`); if (!session) throw new Error('No active session found'); if (!session.access_token) throw new Error('No access token in session'); if (!session.user) throw new Error('No user in session'); if (!session.user.id) throw new Error('No user ID in session'); // Validate token structure (JWT has 3 parts separated by dots) const tokenParts = session.access_token.split('.'); if (tokenParts.length !== 3) { throw new Error(`Invalid JWT structure: expected 3 parts, got ${tokenParts.length}`); } // Check expiration if (session.expires_at && session.expires_at < Date.now() / 1000) { throw new Error('Session token is expired'); } const duration = Date.now() - startTime; return { id: 'auth-001', name: 'User Session Validation', suite: 'Authentication & Authorization', status: 'pass', duration, timestamp: new Date().toISOString(), details: { userId: session.user.id, email: session.user.email, expiresAt: session.expires_at, aal: (session.user as any).aal || 'aal1' } }; } catch (error) { const duration = Date.now() - startTime; return { id: 'auth-001', name: 'User Session Validation', suite: 'Authentication & Authorization', status: 'fail', duration, error: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString() }; } } }, { id: 'auth-002', name: 'Role-Based Access Control (RBAC)', description: 'Tests role checks are consistent across hooks and database functions', run: async (): Promise => { const startTime = Date.now(); try { const { data: { user } } = await supabase.auth.getUser(); if (!user) throw new Error('No authenticated user'); // Query user_roles table const { data: roles, error: rolesError } = await supabase .from('user_roles') .select('role') .eq('user_id', user.id); if (rolesError) throw new Error(`Failed to fetch roles: ${rolesError.message}`); // Test is_moderator() database function const { data: isMod, error: modError } = await supabase .rpc('is_moderator', { _user_id: user.id }); if (modError) throw new Error(`is_moderator() failed: ${modError.message}`); // Test is_superuser() database function const { data: isSuper, error: superError } = await supabase .rpc('is_superuser', { _user_id: user.id }); if (superError) throw new Error(`is_superuser() failed: ${superError.message}`); // Validate consistency const hasModRole = roles?.some(r => ['moderator', 'admin', 'superuser'].includes(r.role)); if (hasModRole !== isMod) { throw new Error(`Inconsistent moderator check: has role=${hasModRole}, is_moderator()=${isMod}`); } const hasSuperRole = roles?.some(r => r.role === 'superuser'); if (hasSuperRole !== isSuper) { throw new Error(`Inconsistent superuser check: has role=${hasSuperRole}, is_superuser()=${isSuper}`); } const duration = Date.now() - startTime; return { id: 'auth-002', name: 'Role-Based Access Control (RBAC)', suite: 'Authentication & Authorization', status: 'pass', duration, timestamp: new Date().toISOString(), details: { roles: roles?.map(r => r.role) || [], isModerator: isMod, isSuperuser: isSuper, consistent: true } }; } catch (error) { const duration = Date.now() - startTime; return { id: 'auth-002', name: 'Role-Based Access Control (RBAC)', suite: 'Authentication & Authorization', status: 'fail', duration, error: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString() }; } } }, { id: 'auth-003', name: 'MFA Factor Detection', description: 'Tests MFA enrollment detection and AAL level', run: async (): Promise => { const startTime = Date.now(); try { const { data: { user } } = await supabase.auth.getUser(); if (!user) throw new Error('No authenticated user'); // Get MFA factors const { data: factors, error: factorsError } = await supabase.auth.mfa.listFactors(); if (factorsError) throw new Error(`Failed to list MFA factors: ${factorsError.message}`); const hasVerifiedFactor = factors?.totp?.some(f => f.status === 'verified') || false; const currentAAL = (user as any).aal || 'aal1'; const duration = Date.now() - startTime; return { id: 'auth-003', name: 'MFA Factor Detection', suite: 'Authentication & Authorization', status: 'pass', duration, timestamp: new Date().toISOString(), details: { hasVerifiedMFA: hasVerifiedFactor, currentAAL: currentAAL, totpFactorCount: factors?.totp?.length || 0, verifiedFactorCount: factors?.totp?.filter(f => f.status === 'verified').length || 0 } }; } catch (error) { const duration = Date.now() - startTime; return { id: 'auth-003', name: 'MFA Factor Detection', suite: 'Authentication & Authorization', status: 'fail', duration, error: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString() }; } } }, { id: 'auth-004', name: 'Banned User Detection', description: 'Tests banned user detection in profiles table', run: async (): Promise => { const startTime = Date.now(); try { const { data: { user } } = await supabase.auth.getUser(); if (!user) throw new Error('No authenticated user'); // Query profile banned status const { data: profile, error: profileError } = await supabase .from('profiles') .select('banned') .eq('user_id', user.id) .single(); if (profileError) throw new Error(`Failed to fetch profile: ${profileError.message}`); if (!profile) throw new Error('No profile found'); // Test is_user_banned() database function const { data: isBanned, error: bannedError } = await supabase .rpc('is_user_banned', { p_user_id: user.id }); if (bannedError) throw new Error(`is_user_banned() failed: ${bannedError.message}`); // Validate consistency if (profile.banned !== isBanned) { throw new Error(`Inconsistent banned check: profile=${profile.banned}, is_user_banned()=${isBanned}`); } const duration = Date.now() - startTime; return { id: 'auth-004', name: 'Banned User Detection', suite: 'Authentication & Authorization', status: 'pass', duration, timestamp: new Date().toISOString(), details: { isBanned: profile.banned, consistent: true } }; } catch (error) { const duration = Date.now() - startTime; return { id: 'auth-004', name: 'Banned User Detection', suite: 'Authentication & Authorization', status: 'fail', duration, error: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, timestamp: new Date().toISOString() }; } } } ] };