56 lines
1.6 KiB
TypeScript
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,
|
|
}),
|
|
);
|
|
}
|