// app/lib/auth-cookies.ts import { NextResponse } from "next/server"; export type TokenBundle = { access_token: string; refresh_token?: string; /** seconds until expiration (Directus style) */ expires?: number; }; export type PublicUser = { id: string; email: string; username: string; }; const ACCESS_COOKIE = "ma_at"; const REFRESH_COOKIE = "ma_rt"; const USER_COOKIE = "ma_user"; /** Derive cookie maxAge (in seconds) for access token */ function accessMaxAgeSec(expires?: number) { // If Directus gave us seconds-until-expiration, use that (clamped) if (typeof expires === "number" && Number.isFinite(expires)) { return Math.max(60, Math.min(expires, 60 * 60 * 24)); // 1 min .. 1 day } // Fallback: 1 hour return 60 * 60; } /** Refresh token lifetime: default ~30 days if present */ function refreshMaxAgeSec() { return 60 * 60 * 24 * 30; } /** Shared secure cookie options (override per cookie when needed) */ function baseOpts(maxAge: number) { return { httpOnly: true as const, sameSite: "lax" as const, secure: true, path: "/", maxAge, }; } /** * Set auth cookies on the provided response. * Returns the SAME response instance with cookies set (typed generically). */ export function setAuthCookies( res: NextResponse, tokens: TokenBundle, user?: PublicUser ): NextResponse { // Access token (httpOnly) const atAge = accessMaxAgeSec(tokens.expires); res.cookies.set(ACCESS_COOKIE, tokens.access_token, baseOpts(atAge)); // Refresh token (httpOnly) if present if (tokens.refresh_token) { res.cookies.set(REFRESH_COOKIE, tokens.refresh_token, baseOpts(refreshMaxAgeSec())); } // Small readable user stub (NOT httpOnly) so client can reflect UI state if desired if (user) { const safeStub = JSON.stringify({ id: user.id, username: user.username, email: user.email, }); res.cookies.set(USER_COOKIE, safeStub, { ...baseOpts(atAge), httpOnly: false, // readable on client }); } return res; } /** Clear all auth cookies (returns the SAME response instance) */ export function clearAuthCookies(res: NextResponse): NextResponse { const opts = { httpOnly: true as const, sameSite: "lax" as const, secure: true, path: "/", maxAge: 0, }; res.cookies.set(ACCESS_COOKIE, "", opts); res.cookies.set(REFRESH_COOKIE, "", opts); // Also clear public user stub res.cookies.set(USER_COOKIE, "", { ...opts, httpOnly: false }); return res; } /** (Optional) Simple helpers if you ever want the names elsewhere */ export const AUTH_COOKIE_KEYS = { access: ACCESS_COOKIE, refresh: REFRESH_COOKIE, user: USER_COOKIE, };