bookwiz.io / app / auth / callback / route.ts
route.ts
Raw
import { createClient } from '@supabase/supabase-js'
import { NextRequest, NextResponse } from 'next/server'

export async function GET(request: NextRequest) {
  const { searchParams, origin, hash } = new URL(request.url)
  const code = searchParams.get('code')
  const error = searchParams.get('error')
  const error_description = searchParams.get('error_description')
  const next = searchParams.get('next') ?? '/'

  console.log('Auth callback received:', {
    code: code ? 'present' : 'missing',
    error,
    error_description,
    origin,
    next,
    hash: hash || 'none',
    fullUrl: request.url
  })

  // Check for OAuth errors first
  if (error) {
    console.error('OAuth error received:', { error, error_description })
    return NextResponse.redirect(`${origin}/auth/auth-code-error?error=${error}&description=${encodeURIComponent(error_description || '')}`)
  }

  // Handle Authorization Code Flow
  if (code) {
    try {
      const supabase = createClient(
        process.env.NEXT_PUBLIC_SUPABASE_URL!,
        process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
      )
      
      console.log('Attempting to exchange code for session...')
      const { data, error: exchangeError } = await supabase.auth.exchangeCodeForSession(code)
      
      if (exchangeError) {
        console.error('Code exchange failed:', {
          error: exchangeError.message,
          status: exchangeError.status,
          details: exchangeError
        })
        return NextResponse.redirect(`${origin}/auth/auth-code-error?error=exchange_failed&description=${encodeURIComponent(exchangeError.message)}`)
      }

      if (data?.session) {
        console.log('Session created successfully for user:', data.user?.email)
        return NextResponse.redirect(`${origin}${next}`)
      } else {
        console.error('No session created despite successful exchange')
        return NextResponse.redirect(`${origin}/auth/auth-code-error?error=no_session`)
      }
      
    } catch (err) {
      console.error('Unexpected error in auth callback:', err)
      return NextResponse.redirect(`${origin}/auth/auth-code-error?error=unexpected&description=${encodeURIComponent(String(err))}`)
    }
  }

  // Handle Implicit Flow (hash-based tokens) - Lightweight version
  console.log('No code found, checking for implicit flow tokens...')
  
  // Create a minimal response that will handle hash-based tokens on the client side
  const html = `
    <!DOCTYPE html>
    <html>
    <head>
      <title>Completing Sign-In...</title>
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
          font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
          display: flex;
          justify-content: center;
          align-items: center;
          min-height: 100vh;
          background: #111827;
          color: white;
        }
        .container {
          text-align: center;
          padding: 2rem;
          background: rgba(30, 41, 59, 0.8);
          border-radius: 1rem;
          border: 1px solid rgba(20, 184, 166, 0.3);
        }
        .spinner {
          width: 24px;
          height: 24px;
          border: 2px solid rgba(20, 184, 166, 0.3);
          border-top: 2px solid #14b8a6;
          border-radius: 50%;
          animation: spin 1s linear infinite;
          margin: 0 auto 1rem;
        }
        @keyframes spin {
          0% { transform: rotate(0deg); }
          100% { transform: rotate(360deg); }
        }
        h1 {
          font-size: 1.25rem;
          margin-bottom: 0.5rem;
          color: #f3f4f6;
        }
        p {
          color: #d1d5db;
          font-size: 0.875rem;
        }
      </style>
    </head>
    <body>
      <div class="container">
        <div class="spinner"></div>
        <h1>Completing Sign-In</h1>
        <p>Setting up your session...</p>
      </div>
      
      <script>
        // Handle hash-based tokens (implicit flow)
        const hash = window.location.hash;
        if (hash && hash.includes('access_token')) {
          console.log('Implicit flow tokens found in hash');
          
          // Parse hash parameters
          const params = new URLSearchParams(hash.substring(1));
          const accessToken = params.get('access_token');
          const refreshToken = params.get('refresh_token');
          const expiresIn = params.get('expires_in');
          
          if (accessToken && refreshToken) {
            // Store tokens in sessionStorage temporarily
            sessionStorage.setItem('supabase_auth_tokens', JSON.stringify({
              access_token: accessToken,
              refresh_token: refreshToken,
              expires_in: expiresIn
            }));
            
            // Redirect to dashboard - AuthProvider will handle session setup
            window.location.href = '${origin}/dashboard';
          } else {
            console.error('Missing required tokens in hash');
            window.location.href = '${origin}/auth/auth-code-error?error=missing_tokens';
          }
        } else {
          console.error('No authentication data found');
          window.location.href = '${origin}/auth/auth-code-error?error=no_auth_data';
        }
      </script>
    </body>
    </html>
  `;

  return new Response(html, {
    headers: { 'Content-Type': 'text/html' },
  });
}