import { COOKIE_NAME, ONE_YEAR_MS } from "@shared/const"; import type { Express, Request, Response } from "express"; import * as db from "../db"; import { getSessionCookieOptions } from "./cookies"; import { sdk } from "./sdk"; function getQueryParam(req: Request, key: string): string | undefined { const value = req.query[key]; return typeof value === "string" ? value : undefined; } export function registerOAuthRoutes(app: Express) { app.get("/api/oauth/callback", async (req: Request, res: Response) => { const code = getQueryParam(req, "code"); const state = getQueryParam(req, "state"); if (!code || !state) { res.status(400).json({ error: "code and state are required" }); return; } try { const tokenResponse = await sdk.exchangeCodeForToken(code, state); const userInfo = await sdk.getUserInfo(tokenResponse.accessToken); if (!userInfo.openId) { res.status(400).json({ error: "openId missing from user info" }); return; } await db.upsertUser({ openId: userInfo.openId, name: userInfo.name || null, email: userInfo.email ?? null, loginMethod: userInfo.loginMethod ?? userInfo.platform ?? null, lastSignedIn: new Date(), }); const sessionToken = await sdk.createSessionToken(userInfo.openId, { name: userInfo.name || "", expiresInMs: ONE_YEAR_MS, }); const cookieOptions = getSessionCookieOptions(req); res.cookie(COOKIE_NAME, sessionToken, { ...cookieOptions, maxAge: ONE_YEAR_MS }); res.redirect(302, "/"); } catch (error) { console.error("[OAuth] Callback failed", error); res.status(500).json({ error: "OAuth callback failed" }); } }); }