diff --git a/app/api/auth/callback/route.ts b/app/api/auth/callback/route.ts index 772852f..3c96b5d 100644 --- a/app/api/auth/callback/route.ts +++ b/app/api/auth/callback/route.ts @@ -3,30 +3,52 @@ import { NextResponse } from "next/server"; export async function GET(req: Request) { const url = new URL(req.url); const code = url.searchParams.get("code"); + const origin = url.origin; + const isHttps = url.protocol === "https:"; - // exchange code for token (Keycloak token endpoint) - const tokenRes = await fetch("https://keycloak.petermaquiran.xyz/realms/tvone/protocol/openid-connect/token", { - method: "POST", - headers: { "Content-Type": "application/x-www-form-urlencoded" }, - body: new URLSearchParams({ - client_id: "tvone-web", - client_secret: "7jQUciQCCf2WRFRe170UANKzGKVWFIkY", - grant_type: "authorization_code", - code: code!, - redirect_uri: "http://localhost:3000/api/auth/callback", - }), - }); + if (!code) { + return NextResponse.redirect(`${origin}/login?error=missing_code`); + } + + const redirectUri = `${origin}/api/auth/callback`; + + const tokenRes = await fetch( + "https://keycloak.petermaquiran.xyz/realms/tvone/protocol/openid-connect/token", + { + method: "POST", + headers: { "Content-Type": "application/x-www-form-urlencoded" }, + body: new URLSearchParams({ + client_id: "tvone-web", + client_secret: "7jQUciQCCf2WRFRe170UANKzGKVWFIkY", + grant_type: "authorization_code", + code, + redirect_uri: redirectUri, + }), + } + ); const text = await tokenRes.text(); - var data = JSON.parse(text); + let data: { access_token?: string; expires_in?: number }; + try { + data = JSON.parse(text) as typeof data; + } catch { + return NextResponse.redirect(`${origin}/login?error=token_parse`); + } - const res = NextResponse.redirect("http://localhost:3000/dashboard"); + if (!tokenRes.ok || !data.access_token) { + console.error("token exchange failed", tokenRes.status, text); + return NextResponse.redirect(`${origin}/login?error=token_exchange`); + } + const res = NextResponse.redirect(`${origin}/dashboard`); + + // Secure cookies are ignored on http:// (e.g. localhost) — browser drops them. res.cookies.set("access_token", data.access_token, { httpOnly: true, - secure: true, + secure: isHttps, sameSite: "lax", path: "/", + maxAge: data.expires_in, }); return res;