fix(sso): enforce root wildcard cookie domain (.atyg.org) across all auth, join, and pass routes

This commit is contained in:
Tyler Gillispie 2026-08-28 00:01:08 -07:00
parent 9c2aa73fdd
commit 267ed4b17b
3 changed files with 22 additions and 42 deletions

View File

@ -201,17 +201,28 @@ export async function isSessionAdmin(
} }
/** /**
* Computes root cookie domain from RP_ID or host. * Computes root wildcard cookie domain from RP_ID, COOKIE_DOMAIN, or host.
* E.g., "auth.atyg.org" -> ".atyg.org" (allowing SSO across *.atyg.org).
*/ */
export function getCookieDomain(customRpId?: string): string { export function getCookieDomain(customRpId?: string): string | undefined {
const rpId = customRpId || Deno.env.get("RP_ID"); const envDomain = Deno.env.get("COOKIE_DOMAIN");
if (rpId) { if (envDomain) {
if (rpId.includes("localhost") || rpId.includes("127.0.0.1")) { return envDomain.startsWith(".") ? envDomain : `.${envDomain}`;
return rpId;
} }
return `.${rpId}`; const targetId = customRpId || Deno.env.get("RP_ID") || "";
if (
!targetId ||
!targetId.includes(".") ||
targetId.includes("localhost") ||
targetId.includes("127.0.0.1")
) {
return undefined;
} }
return ""; const parts = targetId.split(".").filter(Boolean);
if (parts.length >= 2) {
return `.${parts.slice(-2).join(".")}`;
}
return `.${targetId}`;
} }
/** /**

View File

@ -9,7 +9,7 @@ import type { AuthenticationResponseJSON } from "jsr:@simplewebauthn/server@13";
import { valkey } from "../../core/valkey.ts"; import { valkey } from "../../core/valkey.ts";
import { getClientIp, publicRateLimiter } from "../../core/middleware.ts"; import { getClientIp, publicRateLimiter } from "../../core/middleware.ts";
import { extractAllSessionIds } from "../../core/session.ts"; import { extractAllSessionIds, getCookieDomain } from "../../core/session.ts";
import { auditWrapper } from "../../core/audit.ts"; import { auditWrapper } from "../../core/audit.ts";
import { import {
@ -30,22 +30,6 @@ const rpID = Deno.env.get("RP_ID") ||
const origin = Deno.env.get("ORIGIN") || const origin = Deno.env.get("ORIGIN") ||
(import.meta.main ? undefined : "http://localhost"); (import.meta.main ? undefined : "http://localhost");
function getCookieDomain(customRpId?: string): string | undefined {
const envDomain = Deno.env.get("COOKIE_DOMAIN");
if (envDomain) {
return envDomain.startsWith(".") ? envDomain : `.${envDomain}`;
}
const targetId = customRpId || Deno.env.get("RP_ID") || "";
if (!targetId || !targetId.includes(".") || targetId === "localhost") {
return undefined;
}
const parts = targetId.split(".").filter(Boolean);
if (parts.length >= 2) {
return `.${parts.slice(-2).join(".")}`;
}
return `.${targetId}`;
}
// UI Route // UI Route
loginRoutes.get("/login", (c) => { loginRoutes.get("/login", (c) => {
return c.html(LoginPageFragment()); return c.html(LoginPageFragment());

View File

@ -10,6 +10,7 @@ import type { RegistrationResponseJSON } from "jsr:@simplewebauthn/server@13";
import { valkey } from "../../core/valkey.ts"; import { valkey } from "../../core/valkey.ts";
import { getClientIp, publicRateLimiter } from "../../core/middleware.ts"; import { getClientIp, publicRateLimiter } from "../../core/middleware.ts";
import { auditWrapper } from "../../core/audit.ts"; import { auditWrapper } from "../../core/audit.ts";
import { getCookieDomain } from "../../core/session.ts";
import { import {
createPasskey, createPasskey,
@ -28,22 +29,6 @@ const rpID = Deno.env.get("RP_ID") ||
const origin = Deno.env.get("ORIGIN") || const origin = Deno.env.get("ORIGIN") ||
(import.meta.main ? undefined : "http://localhost"); (import.meta.main ? undefined : "http://localhost");
function getCookieDomain(customRpId?: string): string | undefined {
const envDomain = Deno.env.get("COOKIE_DOMAIN");
if (envDomain) {
return envDomain.startsWith(".") ? envDomain : `.${envDomain}`;
}
const targetId = customRpId || Deno.env.get("RP_ID") || "";
if (!targetId || !targetId.includes(".") || targetId === "localhost") {
return undefined;
}
const parts = targetId.split(".").filter(Boolean);
if (parts.length >= 2) {
return `.${parts.slice(-2).join(".")}`;
}
return `.${targetId}`;
}
// UI Route // UI Route
registerRoutes.get("/register", (c) => { registerRoutes.get("/register", (c) => {
const code = c.req.query("code") || ""; const code = c.req.query("code") || "";