/** * GET /api/theia-auth * * Traefik ForwardAuth endpoint for theia.vibnai.com. * * Traefik calls this URL for every request to the Theia IDE, forwarding * the user's Cookie header via authRequestHeaders. We validate the * NextAuth database session (strategy: "database") by looking up the * session token directly in Postgres — avoiding Prisma / authOptions * imports that cause build-time issues under --network host. * * Returns: * 200 — valid session, Traefik lets the request through * 302 — no/expired session, redirect browser to Vibn login */ import { NextRequest, NextResponse } from 'next/server'; import { query } from '@/lib/db-postgres'; export const dynamic = 'force-dynamic'; const APP_URL = process.env.NEXTAUTH_URL ?? 'https://vibnai.com'; const THEIA_URL = 'https://theia.vibnai.com'; // NextAuth v4 uses these cookie names for database sessions const SESSION_COOKIE_NAMES = [ '__Secure-next-auth.session-token', // HTTPS (production) 'next-auth.session-token', // HTTP fallback ]; export async function GET(request: NextRequest) { // Extract session token from cookies let sessionToken: string | null = null; for (const name of SESSION_COOKIE_NAMES) { const val = request.cookies.get(name)?.value; if (val) { sessionToken = val; break; } } if (!sessionToken) { return redirectToLogin(request); } // Look up session in Postgres (NextAuth stores sessions in the "sessions" table) let userEmail: string | null = null; let userName: string | null = null; try { const rows = await query<{ email: string; name: string }>( `SELECT u.email, u.name FROM sessions s JOIN users u ON u.id = s.user_id WHERE s.session_token = $1 AND s.expires > NOW() LIMIT 1`, [sessionToken], ); if (rows.length > 0) { userEmail = rows[0].email; userName = rows[0].name; } } catch (err) { console.error('[theia-auth] DB error:', err); return redirectToLogin(request); } if (!userEmail) { return redirectToLogin(request); } // Session valid — pass user identity to Theia via response headers return new NextResponse(null, { status: 200, headers: { 'X-Auth-Email': userEmail, 'X-Auth-Name': userName ?? '', }, }); } function redirectToLogin(request: NextRequest): NextResponse { // Traefik ForwardAuth sets X-Forwarded-Host to the auth service's host (vibnai.com), // not the original request host (theia.vibnai.com). Use THEIA_URL directly as the // destination so the user returns to Theia after logging in. const loginUrl = `${APP_URL}/auth?callbackUrl=${encodeURIComponent(THEIA_URL)}`; return NextResponse.redirect(loginUrl, { status: 302 }); }