Files
WRNexusJS/packages/security/src/cookies.ts
T
2026-08-01 10:04:42 +05:30

56 lines
1.6 KiB
TypeScript

import type { Context, CookieOptions } from "@wrnexus/core";
import { SecurityError } from "./errors.ts";
export interface SecureCookieOptions extends CookieOptions {
hostOnly?: boolean;
}
export function secureCookieOptions(
ctx: Pick<Context, "url">,
options: SecureCookieOptions = {},
): CookieOptions {
const secure = options.secure ?? ctx.url.protocol === "https:";
const sameSite = options.sameSite ?? "Lax";
if (sameSite.toString().toLowerCase() === "none" && !secure) {
throw new SecurityError(
"WRN-SEC-COOKIE-SAMESITE",
"SameSite=None cookies must also use Secure.",
);
}
if (options.hostOnly && options.domain) {
throw new SecurityError("WRN-SEC-COOKIE-HOST", "Host-only cookies cannot set Domain.");
}
return {
path: options.path ?? "/",
...options,
domain: options.hostOnly ? undefined : options.domain,
httpOnly: options.httpOnly ?? true,
secure,
sameSite,
};
}
export function setSecureCookie(
ctx: Pick<Context, "url" | "cookies">,
name: string,
value: string,
options: SecureCookieOptions = {},
): void {
if (name.startsWith("__Host-") && (options.domain || (options.path && options.path !== "/"))) {
throw new SecurityError(
"WRN-SEC-COOKIE-HOST-PREFIX",
"__Host- cookies require Path=/ and must not set Domain.",
);
}
ctx.cookies.set(
name,
value,
secureCookieOptions(ctx, {
...options,
hostOnly: name.startsWith("__Host-") || options.hostOnly,
path: name.startsWith("__Host-") ? "/" : options.path,
secure: name.startsWith("__Host-") ? true : options.secure,
}),
);
}