auth-cookies build error fix

This commit is contained in:
makearmy 2025-09-26 15:34:24 -04:00
parent 7b2b185ed9
commit 514982d009
4 changed files with 213 additions and 133 deletions

View file

@ -4,7 +4,7 @@ import { NextResponse } from "next/server";
export type TokenBundle = {
access_token: string;
refresh_token?: string;
/** Directus returns seconds-until-expiration */
/** seconds until expiration (Directus style) */
expires?: number;
};
@ -14,55 +14,89 @@ export type PublicUser = {
username: string;
};
export const ACCESS_COOKIE = "ma_access";
export const REFRESH_COOKIE = "ma_refresh";
const ACCESS_COOKIE = "ma_at";
const REFRESH_COOKIE = "ma_rt";
const USER_COOKIE = "ma_user";
/**
* Mutates `res` in-place to set auth cookies.
* Keeps tokens HttpOnly; sets SameSite=Lax; Secure for HTTPS.
*/
export function setAuthCookies(
res: NextResponse,
tokens: TokenBundle,
_user?: PublicUser
): void {
const maxAge =
typeof tokens.expires === "number" ? tokens.expires : 60 * 60 * 12; // 12h default
if (tokens.access_token) {
res.cookies.set(ACCESS_COOKIE, tokens.access_token, {
httpOnly: true,
sameSite: "lax",
secure: true,
path: "/",
maxAge,
});
}
if (tokens.refresh_token) {
// If Directus doesnt give a separate TTL, just make it longer than access (fallback 30d)
const refreshMaxAge =
typeof tokens.expires === "number" ? tokens.expires * 4 : 60 * 60 * 24 * 30;
res.cookies.set(REFRESH_COOKIE, tokens.refresh_token, {
httpOnly: true,
sameSite: "lax",
secure: true,
path: "/",
maxAge: refreshMaxAge,
});
/** 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;
}
/** Mutates `res` in-place to clear both auth cookies. */
export function clearAuthCookies(res: NextResponse): void {
const opts = {
httpOnly: true,
/** 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: 0, // expire immediately
maxAge,
};
}
/**
* Set auth cookies on the provided response.
* Returns the SAME response instance with cookies set (typed generically).
*/
export function setAuthCookies<T>(
res: NextResponse<T>,
tokens: TokenBundle,
user?: PublicUser
): NextResponse<T> {
// 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<T>(res: NextResponse<T>): NextResponse<T> {
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,
};