import { sql } from "@/lib/db"; import { issueSession, toProfile } from "@/lib/users"; const TOKENINFO_URL = "https://oauth2.googleapis.com/tokeninfo?id_token="; type GoogleTokenInfo = { aud?: string; sub?: string; email?: string; email_verified?: string | boolean; name?: string; exp?: string; error_description?: string; }; export async function POST(req: Request) { const { idToken } = await req.json(); if (!idToken || typeof idToken !== "string") { return Response.json({ error: "Missing idToken." }, { status: 400 }); } const audience = process.env.GOOGLE_OAUTH_CLIENT_ID ?? process.env.EXPO_PUBLIC_GOOGLE_AUTH_WEB_CLIENT_ID; if (!audience) { return Response.json( { error: "Server is missing GOOGLE_OAUTH_CLIENT_ID." }, { status: 500 }, ); } try { const response = await fetch(`${TOKENINFO_URL}${idToken}`); if (!response.ok) { return Response.json( { error: "Invalid Google token." }, { status: 401 }, ); } const info = (await response.json()) as GoogleTokenInfo; if ( info.aud !== audience || !info.sub || !info.email || (info.email_verified !== true && info.email_verified !== "true") || (info.exp && Number(info.exp) * 1000 < Date.now()) ) { return Response.json( { error: "Google token failed validation." }, { status: 401 }, ); } const name = info.name?.trim() || info.email.split("@")[0]; const rows = await sql<{ id: string; name: string; email: string; role: string | null; }>` INSERT INTO users (name, email, google_sub, email_verified) VALUES (${name}, ${info.email.toLowerCase()}, ${info.sub}, TRUE) ON CONFLICT (email) DO UPDATE SET google_sub = EXCLUDED.google_sub, email_verified = TRUE, name = CASE WHEN users.name = split_part(users.email, '@', 1) THEN EXCLUDED.name ELSE users.name END RETURNING id, name, email, role `; const user = rows[0]; if (!user) { return Response.json({ error: "Could not create user." }, { status: 500 }); } const session = issueSession(user); return Response.json({ data: { token: session.token, user: toProfile(user) }, }); } catch (error) { console.error("[GOOGLE_AUTH]: ", error); return Response.json({ error: "Internal Server Error" }, { status: 500 }); } }