mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 06:40:50 +00:00
fix(auth): issue a Secure session cookie under the __Host- prefix
A cookie named healthlog_session can be set by any host under the same registrable domain, so a page on a sibling subdomain could plant a session of its choosing in a visitor's browser. Wherever the session cookie is Secure it is now issued as __Host-healthlog_session, which a browser accepts only from this exact host. Plain-HTTP installs, which cannot hold a __Host- cookie, keep the old name. Nobody is signed out: the old name is still read after the new one, a session found under it is re-issued under the new name and the old cookie expired, and a browser holding the new cookie is never answered from the old one. Every reader and writer goes through one module, which the proxy, the native-caller check, the native handoff and the OIDC callback use; the session-surface guard follows the new reader.
This commit is contained in:
1 parent
d3257a1b6b
commit
5a875d2840
19 files changed
+348
-88
No files matched your search
@@ -22,7 +22,7 @@ import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import { GET } from "@/app/api/auth/oidc/callback/route";
|
||||
import { encrypt } from "@/lib/crypto";
|
||||
import { OIDC_STATE_COOKIE } from "@/lib/auth/oidc-cookie";
|
||||
import { SESSION_COOKIE_NAME } from "@/lib/auth/session";
|
||||
import { sessionCookieName } from "@/lib/auth/session-cookie";
|
||||
|
||||
import { anonymous, callRoute } from "../../../kit/call-route";
|
||||
import { makeSession, makeUser } from "../../../kit/factories";
|
||||
@@ -312,7 +312,7 @@ describe("GET /api/auth/oidc/callback parity", () => {
|
||||
const session = await makeSession(first);
|
||||
await step("12-duplicate-with-session", {
|
||||
state: null,
|
||||
cookies: { [SESSION_COOKIE_NAME]: session.cookie },
|
||||
cookies: { [sessionCookieName()]: session.cookie },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -382,7 +382,7 @@ describe("GET /api/auth/oidc/callback parity", () => {
|
||||
identity("sub-claimer", "claimer@example.test", true);
|
||||
await step("23-claim-signed-in", {
|
||||
state: { intent: "claim", claimBinding: "binding-1" },
|
||||
cookies: { [SESSION_COOKIE_NAME]: session.cookie },
|
||||
cookies: { [sessionCookieName()]: session.cookie },
|
||||
});
|
||||
await step("24-claim-no-cookie", {
|
||||
state: { intent: "claim", claimBinding: "binding-1" },
|
||||
|
||||
@@ -25,7 +25,7 @@ import { NextRequest } from "next/server";
|
||||
import type { z } from "zod";
|
||||
|
||||
import { openApiComponents, openApiPaths } from "@/lib/openapi/routes";
|
||||
import { SESSION_COOKIE_NAME } from "@/lib/auth/session";
|
||||
import { sessionCookieName } from "@/lib/auth/session-cookie";
|
||||
|
||||
import { cookieJar, headerJar } from "../integration/mock-next-headers";
|
||||
import type { BearerFixture, SessionFixture } from "./factories";
|
||||
@@ -122,7 +122,7 @@ export async function callRoute<T = unknown>(
|
||||
headers.set("authorization", `Bearer ${options.auth.token}`);
|
||||
}
|
||||
if (options.auth.kind === "session") {
|
||||
headers.set("cookie", `${SESSION_COOKIE_NAME}=${options.auth.cookie}`);
|
||||
headers.set("cookie", `${sessionCookieName()}=${options.auth.cookie}`);
|
||||
}
|
||||
|
||||
// `next/headers` is served from the shared jars; fill them for this call
|
||||
@@ -133,7 +133,7 @@ export async function callRoute<T = unknown>(
|
||||
const savedHeaders = new Map(headerJar);
|
||||
cookieJar.clear();
|
||||
if (options.auth.kind === "session") {
|
||||
cookieJar.set(SESSION_COOKIE_NAME, options.auth.cookie);
|
||||
cookieJar.set(sessionCookieName(), options.auth.cookie);
|
||||
}
|
||||
headerJar.delete("authorization");
|
||||
headers.forEach((value, name) => headerJar.set(name.toLowerCase(), value));
|
||||
|
||||
@@ -27,7 +27,8 @@ import type {
|
||||
Workout,
|
||||
} from "@/generated/prisma/client";
|
||||
import { prisma } from "@/lib/db";
|
||||
import { createSession, SESSION_COOKIE_NAME } from "@/lib/auth/session";
|
||||
import { createSession } from "@/lib/auth/session";
|
||||
import { sessionCookieName } from "@/lib/auth/session-cookie";
|
||||
import { issueApiToken } from "@/lib/auth/issue-token";
|
||||
|
||||
import { cookieJar } from "../integration/mock-next-headers";
|
||||
@@ -91,7 +92,7 @@ export async function makeSession(
|
||||
"healthlog-kit",
|
||||
options.mfaVerifiedAt ?? null,
|
||||
);
|
||||
const cookie = cookieJar.get(SESSION_COOKIE_NAME);
|
||||
const cookie = cookieJar.get(sessionCookieName());
|
||||
cookieJar.clear();
|
||||
for (const [name, value] of before) cookieJar.set(name, value);
|
||||
if (!cookie) {
|
||||
|
||||
Reference in new issue
Block a user