import { NextResponse } from "next/server"; import { queryOne } from "@/lib/db"; import { createSession } from "@/lib/auth/session"; import { isConfigured } from "@/lib/auth/oidc-config"; import { consumeState } from "@/lib/auth/oidc-state"; import { discover, exchangeCode, resolveEmail, verifyIdToken } from "@/lib/auth/oidc-client"; import type { Role } from "@/lib/types"; function errorRedirect(request: Request, code: string): NextResponse { return NextResponse.redirect(new URL(`/login?error=${code}`, request.url)); } // GET statt POST: der Identity Provider leitet den Browser per // Redirect hierher (Cross-Origin-Navigation) – die übliche // isSameOrigin()-CSRF-Prüfung greift hier nicht und wird bewusst nicht // verwendet. Die Absicherung ist der einmalig verwendbare state- // Parameter (Standard-OIDC-Mechanismus, siehe lib/auth/oidc-state.ts). export async function GET(request: Request) { if (!isConfigured()) { return errorRedirect(request, "oidc_failed"); } const url = new URL(request.url); const code = url.searchParams.get("code"); const state = url.searchParams.get("state"); const idpError = url.searchParams.get("error"); if (idpError || !code || !state) { return errorRedirect(request, "oidc_failed"); } const stored = await consumeState(state); if (!stored) { return errorRedirect(request, "oidc_invalid_state"); } try { const discovery = await discover(); const { idToken, accessToken } = await exchangeCode(discovery, code, stored.codeVerifier); const payload = await verifyIdToken(discovery, idToken, stored.nonce); const email = await resolveEmail(discovery, payload, accessToken); if (!email) { return errorRedirect(request, "oidc_failed"); } const user = await queryOne<{ id: string; role: Role }>( "select id, role from users where email = $1", [email.trim().toLowerCase()] ); if (!user) { return errorRedirect(request, "oidc_unknown_user"); } await createSession(user.id); return NextResponse.redirect( new URL(user.role === "admin" ? "/admin" : "/intern", request.url) ); } catch { return errorRedirect(request, "oidc_failed"); } }