Files
blackcanyontickets/AUTHENTICATION_FIX.md
dzinesco f4f929912d fix: Resolve authentication login loop preventing dashboard access
## Problem
Users experienced infinite login loops where successful authentication would
redirect to dashboard, then immediately redirect back to login page.

## Root Cause
Client-server authentication mismatch due to httpOnly cookies:
- Login API sets httpOnly cookies using server-side Supabase client 
- Dashboard server reads httpOnly cookies correctly 
- Dashboard client script tried to read httpOnly cookies using client-side Supabase 

## Solution
1. Fixed Admin Dashboard: Removed non-existent `is_super_admin` column references
2. Created Auth Check API: Server-side auth validation for client scripts
3. Updated Admin API Router: Uses auth check API instead of client-side Supabase

## Key Changes
- src/pages/admin/dashboard.astro: Fixed database queries
- src/pages/api/admin/auth-check.ts: NEW server-side auth validation API
- src/lib/admin-api-router.ts: Uses API calls instead of client-side auth
- src/pages/api/auth/session.ts: Return 200 status for unauthenticated users
- src/pages/login.astro: Enhanced cache clearing and session management

## Testing
- Automated Playwright tests validate end-to-end login flow
- Manual testing confirms successful login without loops

## Documentation
- AUTHENTICATION_FIX.md: Complete technical documentation
- CLAUDE.md: Updated with authentication system notes

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-13 10:19:04 -06:00

6.8 KiB

Authentication Login Loop Fix

Problem Description

Users experienced a login loop where:

  1. User enters valid credentials and login succeeds
  2. User gets redirected to dashboard initially
  3. Dashboard immediately redirects back to login page
  4. This creates an infinite loop preventing access to the dashboard

Root Cause Analysis

The issue was a client-server authentication mismatch in the authentication system:

The Problem Flow

  1. Login API (/src/pages/api/auth/login.ts): Sets httpOnly cookies using server-side Supabase client
  2. Dashboard Server (/src/pages/admin/dashboard.astro): Reads httpOnly cookies using server-side Supabase client
  3. Dashboard Client Script (/src/lib/admin-api-router.ts): Attempts to read httpOnly cookies using client-side Supabase client

Technical Details

  • httpOnly cookies: Cannot be accessed by client-side JavaScript for security
  • Client-side Supabase: supabase.auth.getSession() fails when cookies are httpOnly
  • Authentication mismatch: Server says "authenticated" but client says "not authenticated"

Secondary Issues Found

  • Missing database column: Admin dashboard tried to select non-existent is_super_admin column
  • Database query failures: Caused authentication to fail even with valid sessions

Solution Implementation

1. Fixed Admin Dashboard Server-Side Auth

File: /src/pages/admin/dashboard.astro

Problem:

.select('role, organization_id, is_super_admin')  // is_super_admin doesn't exist

Fix:

.select('role, organization_id')  // Removed non-existent column

2. Created Server-Side Auth Check API

File: /src/pages/api/admin/auth-check.ts (NEW)

Purpose: Provides a server-side authentication check that client-side code can call

Features:

  • Uses server-side Supabase client with access to httpOnly cookies
  • Returns authentication status and user information
  • Handles admin role verification
  • Provides consistent error handling

API Response:

{
  authenticated: boolean,
  isAdmin: boolean,
  user?: {
    id: string,
    email: string,
    name: string
  },
  organizationId?: string,
  error?: string
}

3. Updated Admin API Router

File: /src/lib/admin-api-router.ts

Before:

// Tried to use client-side Supabase (fails with httpOnly cookies)
const { data: { session }, error } = await supabase.auth.getSession();

After:

// Uses server-side auth check API
const response = await fetch('/api/admin/auth-check', {
  method: 'GET',
  credentials: 'include'
});

Testing & Validation

Playwright Automated Testing

Used Playwright to create automated end-to-end tests that:

  1. Navigate to login page
  2. Fill credentials and submit form
  3. Monitor network requests and cookie setting
  4. Verify final redirect destination
  5. Capture screenshots at each step

Test Results

  • Before Fix: Infinite redirect loop between login and dashboard
  • After Fix: Successful login and stable dashboard access

Test Files

  • /home/tyler/apps/bct-whitelabel/test-login.js: Playwright test script
  • /home/tyler/apps/bct-whitelabel/test-recordings/: Screenshots and recordings

Implementation Details

Authentication Flow (Fixed)

  1. User submits login form

    • Client sends credentials to /api/auth/login
    • Login API validates credentials with Supabase
    • Sets httpOnly session cookies
    • Returns success + redirect path
  2. User redirected to dashboard

    • Server-side auth check reads httpOnly cookies
    • Validates session and admin status
    • Renders dashboard if authorized
  3. Dashboard client script initializes

    • Calls /api/admin/auth-check endpoint
    • Server validates httpOnly cookies
    • Returns authentication status to client
    • Client proceeds with dashboard functionality

Security Considerations

  • httpOnly cookies: Maintain security by preventing client-side access
  • Server-side validation: All authentication checks use server-side Supabase client
  • API endpoint security: Auth check API validates session before returning user data

Files Modified

Primary Fixes

  1. /src/pages/admin/dashboard.astro

    • Fixed database query (removed is_super_admin column)
    • Added proper error handling for user lookup
    • Line 20: select('role, organization_id') instead of select('role, organization_id, is_super_admin')
  2. /src/pages/api/admin/auth-check.ts (NEW)

    • Created server-side authentication check API
    • Handles admin role verification
    • Returns user information for client-side use
  3. /src/lib/admin-api-router.ts

    • Replaced client-side Supabase auth with API call
    • Line 17-20: Uses fetch('/api/admin/auth-check') instead of supabase.auth.getSession()

Supporting Fixes

  1. /src/pages/api/auth/session.ts

    • Changed unauthenticated response from 401 to 200 status
    • Prevents browser console errors for normal "not logged in" state
  2. /src/pages/login.astro

    • Enhanced session cache clearing after successful login
    • Reduced cache duration for more responsive auth checks
    • Added support for force refresh URL parameters

Monitoring & Maintenance

Key Metrics to Monitor

  • Login success rate: Should be near 100% for valid credentials
  • Dashboard load time: Should not have authentication delays
  • Session API calls: Should not hit rate limits

Future Improvements

  • Add is_super_admin column: If super admin functionality is needed
  • Implement Redis caching: For better session caching in production
  • Add authentication middleware: For more centralized auth handling

Troubleshooting

Common Issues

  1. "User not authenticated or not admin" error

    • Check if user has admin role in database
    • Verify session cookies are being set correctly
  2. 404 on /api/admin/auth-check

    • Ensure the new API endpoint file was deployed
    • Check that the file is in the correct location
  3. Still getting login loops

    • Clear browser cookies and sessionStorage
    • Check if admin dashboard is using the updated admin-api-router

Debug Commands

# Check user role in database
psql -c "SELECT email, role FROM users WHERE email = 'user@example.com';"

# Test auth check API directly
curl -H "Cookie: sb-..." http://localhost:3000/api/admin/auth-check

# Monitor auth-related logs
docker logs bct-astro-dev | grep -E "(LOGIN|AUTH|ADMIN)"

Impact Summary

Fixed: Login loop preventing dashboard access
Improved: Authentication system reliability
Enhanced: Error handling and debugging capabilities
Maintained: Security with httpOnly cookies
Added: Automated testing for authentication flow

The authentication system now works seamlessly across all user types and provides a stable foundation for the application.