Pathfinder Docs

Documentation Preview

Middleware Session Fix

Source: `docs/architecture/MIDDLEWARE_FIX.md`View on GitHub

Middleware Session Fix

Date: November 23, 2025
Issue: Users being redirected to /crm/login on every route navigation despite being logged in

Last verified against code: 2025-02-03

Problem

The middleware was not properly maintaining Supabase session cookies across requests, causing:

  • Session cookies not being updated in responses
  • Users appearing unauthenticated on each new route
  • Constant redirects to login page

Root Causes

1. Cookie Handling Issue

The middleware was creating a new NextResponse object in the cookie set and remove methods, which would discard the previous response and its cookies.

Before (Broken):

set(name: string, value: string, options: CookieOptions) {
  request.cookies.set({ name, value, ...options })
  response = NextResponse.next({ // ❌ Creates NEW response, discards old cookies
    request: { headers: request.headers },
  })
  response.cookies.set({ name, value, ...options })
}

After (Fixed):

set(name: string, value: string, options: CookieOptions) {
  request.cookies.set({ name, value, ...options })
  // ✅ Update existing response instead of creating new one
  response.cookies.set({ name, value, ...options })
}

2. Incomplete Route Protection

The /intake and /crm routes were not included in middleware protection, causing:

  • Session not being refreshed when accessing these routes
  • Inconsistent auth state when navigating between routes

Changes Made

1. Fixed Cookie Handling in Middleware

File: middleware.ts

export async function middleware(request: NextRequest) {
  let response = NextResponse.next({
    request, // Simplified
  })

  const supabase = createServerClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
    {
      cookies: {
        get(name: string) {
          return request.cookies.get(name)?.value
        },
        set(name: string, value: string, options: CookieOptions) {
          // Update request cookies
          request.cookies.set({ name, value, ...options })
          // Update response cookies (don't create new response)
          response.cookies.set({ name, value, ...options })
        },
        remove(name: string, options: CookieOptions) {
          request.cookies.set({ name, value: '', ...options })
          response.cookies.set({ name, value: '', ...options })
        },
      },
    }
  )

  // This call will now properly update cookies in response
  const { data: { user } } = await supabase.auth.getUser()
  
  // ... rest of middleware
}

2. Added Missing Routes to Protection

Added routes:

  • /intake (staff version) - Now protected at middleware level
  • /crm/* (dashboard and related routes) - Now consistently protected

Excluded routes:

  • /intake/self-service - Remains public for future use
  • /crm/login - Login page
  • /crm/logout - Logout route
const isCRMRoute = request.nextUrl.pathname.startsWith('/resources') ||
                   request.nextUrl.pathname.startsWith('/clients') ||
                   request.nextUrl.pathname.startsWith('/referrals') ||
                   (request.nextUrl.pathname.startsWith('/intake') && 
                    !request.nextUrl.pathname.startsWith('/intake/self-service')) ||
                   (request.nextUrl.pathname === '/crm' || 
                    request.nextUrl.pathname.startsWith('/crm/'))

Related Files

  • middleware.ts - Main middleware with fixes
  • lib/supabase-server.ts - Server-side Supabase client
  • lib/supabase-client.ts - Client-side Supabase client
  • app/crm/login/page.tsx - Login page
  • Access Control

Status: ✅ Fixed
Impact: All CRM routes now maintain session properly


Use links in each imported doc to open its source.