2025-06-30 18:58:24 +02:00
|
|
|
/*
|
|
|
|
SPDX-FileCopyrightText: © 2025 Hornwitser <code@hornwitser.no>
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
*/
|
2025-03-06 22:07:51 +01:00
|
|
|
import type { H3Event } from "h3";
|
2025-05-20 00:43:29 +02:00
|
|
|
import * as fs from "node:fs/promises";
|
2025-03-06 22:07:51 +01:00
|
|
|
|
|
|
|
let cachedCookieSecret: CryptoKey;
|
|
|
|
export async function useCookieSecret(event: H3Event) {
|
|
|
|
if (cachedCookieSecret)
|
|
|
|
return cachedCookieSecret;
|
|
|
|
|
|
|
|
const runtimeConfig = useRuntimeConfig(event);
|
2025-05-20 00:43:29 +02:00
|
|
|
if (!runtimeConfig.cookieSecretKeyFile) throw new Error("NUXT_COOKIE_SECRET_KEY_FILE not set.");
|
2025-03-06 22:07:51 +01:00
|
|
|
return cachedCookieSecret = await crypto.subtle.importKey(
|
|
|
|
"raw",
|
2025-05-20 00:43:29 +02:00
|
|
|
Buffer.from(await fs.readFile(runtimeConfig.cookieSecretKeyFile, "utf-8"), "base64url"),
|
2025-03-06 22:07:51 +01:00
|
|
|
{ name: "HMAC", hash: "SHA-256" },
|
|
|
|
false,
|
|
|
|
["sign", "verify"],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2025-03-11 16:30:51 +01:00
|
|
|
export async function setSignedCookie(event: H3Event, name: string, value: string, maxAge?: number) {
|
2025-03-06 22:07:51 +01:00
|
|
|
const secret = await useCookieSecret(event);
|
2025-04-02 23:13:06 +02:00
|
|
|
const signature = await crypto.subtle.sign("HMAC", secret, Buffer.from(`${name}=${value}`));
|
2025-03-06 22:07:51 +01:00
|
|
|
const cookie = `${value}.${Buffer.from(signature).toString("base64url")}`
|
2025-03-11 16:30:51 +01:00
|
|
|
setCookie(event, name, cookie, { httpOnly: true, secure: true, sameSite: true, maxAge });
|
2025-03-06 22:07:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
export async function getSignedCookie(event: H3Event, name: string) {
|
|
|
|
const cookie = getCookie(event, name);
|
|
|
|
if (!cookie)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const rightDot = cookie.lastIndexOf(".");
|
|
|
|
if (rightDot === -1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const value = cookie.slice(0, rightDot);
|
|
|
|
const secret = await useCookieSecret(event);
|
|
|
|
const signature = Buffer.from(cookie.slice(rightDot + 1), "base64url");
|
2025-04-02 23:13:06 +02:00
|
|
|
const valid = await crypto.subtle.verify("HMAC", secret, signature, Buffer.from(`${name}=${value}`));
|
2025-03-06 22:07:51 +01:00
|
|
|
if (!valid)
|
|
|
|
return
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|