fix(auth): eliminate conflicting deleteCookie and add created_at to users table

This commit is contained in:
Tyler Gillispie 2026-08-24 17:14:07 -07:00
parent 250e9c625b
commit 4b78f70efd
7 changed files with 18 additions and 13 deletions

View File

@ -216,7 +216,7 @@ export async function isGlobalAdmin(userId: string): Promise<boolean> {
// Check 2: First registered user in system fallback // Check 2: First registered user in system fallback
const firstUser = await sqlWrapper.sql` const firstUser = await sqlWrapper.sql`
SELECT id FROM users ORDER BY created_at ASC LIMIT 1 SELECT id FROM users ORDER BY created_at ASC NULLS LAST, username ASC LIMIT 1
`.then((res: any) => res[0]); `.then((res: any) => res[0]);
if (firstUser && firstUser.id === userId) { if (firstUser && firstUser.id === userId) {

View File

@ -34,12 +34,14 @@ export async function initDb(): Promise<void> {
id UUID PRIMARY KEY DEFAULT gen_random_uuid(), id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
username TEXT UNIQUE NOT NULL, username TEXT UNIQUE NOT NULL,
display_name TEXT, display_name TEXT,
account_status TEXT DEFAULT 'pending' account_status TEXT DEFAULT 'pending',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
); );
`; `;
try { try {
await sql`ALTER TABLE users ALTER COLUMN account_status SET DEFAULT 'pending'`; await sql`ALTER TABLE users ALTER COLUMN account_status SET DEFAULT 'pending'`;
await sql`ALTER TABLE users ADD COLUMN IF NOT EXISTS created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()`;
} catch { } catch {
// Ignore if unsupported // Ignore if unsupported
} }

View File

@ -520,7 +520,9 @@ Deno.test("Tier 1 & 2: POST /api/guests/sandbox - Creates guest session", async
Deno.test("Logout Return-Path Validation", async (t) => { Deno.test("Logout Return-Path Validation", async (t) => {
await t.step("GET /logout preserves valid redirect", async () => { await t.step("GET /logout preserves valid redirect", async () => {
const req = new Request("http://localhost/logout?redirect=https://ed-droid.atyg.org/"); const req = new Request(
"http://localhost/logout?redirect=https://ed-droid.atyg.org/",
);
const res = await app.fetch(req); const res = await app.fetch(req);
assertEquals(res.status, 302); assertEquals(res.status, 302);
assertEquals( assertEquals(

View File

@ -366,8 +366,6 @@ app.post("/api/guests/sandbox", async (c) => {
const cookieDomain = getCookieDomain(rpID); const cookieDomain = getCookieDomain(rpID);
deleteCookie(c, "session_id", { path: "/" });
setCookie(c, "session_id", sessionId, { setCookie(c, "session_id", sessionId, {
domain: cookieDomain, domain: cookieDomain,
path: "/", path: "/",
@ -834,9 +832,6 @@ app.post("/api/login/verify", async (c) => {
const cookieDomain = getCookieDomain(rpID); const cookieDomain = getCookieDomain(rpID);
// Clear any existing host-only cookie that might shadow the wildcard domain cookie
deleteCookie(c, "session_id", { path: "/" });
setCookie(c, "session_id", sessionId, { setCookie(c, "session_id", sessionId, {
domain: cookieDomain, domain: cookieDomain,
path: "/", path: "/",
@ -1129,7 +1124,10 @@ app.get("/api/forward-auth", async (c) => {
// If a browser is requesting a webpage on an unregistered domain, seamlessly redirect to unregistered error view // If a browser is requesting a webpage on an unregistered domain, seamlessly redirect to unregistered error view
if (accept.includes("text/html")) { if (accept.includes("text/html")) {
const loginDomain = rpID || "auth.atyg.org"; const loginDomain = rpID || "auth.atyg.org";
c.header("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0"); c.header(
"Cache-Control",
"no-store, no-cache, must-revalidate, max-age=0",
);
return c.redirect( return c.redirect(
`https://${loginDomain}/errors/unregistered?host=${ `https://${loginDomain}/errors/unregistered?host=${
encodeURIComponent(host) encodeURIComponent(host)
@ -1192,7 +1190,10 @@ app.get("/api/forward-auth", async (c) => {
// If a browser is requesting a webpage, seamlessly redirect to login // If a browser is requesting a webpage, seamlessly redirect to login
if (accept.includes("text/html")) { if (accept.includes("text/html")) {
const loginDomain = rpID || "auth.atyg.org"; const loginDomain = rpID || "auth.atyg.org";
c.header("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0"); c.header(
"Cache-Control",
"no-store, no-cache, must-revalidate, max-age=0",
);
return c.redirect( return c.redirect(
`https://${loginDomain}/login?redirect=${ `https://${loginDomain}/login?redirect=${
encodeURIComponent(originalUrl) encodeURIComponent(originalUrl)

View File

@ -159,7 +159,7 @@ export const AuthenticatedLayout = ({
</style> </style>
<script src="https://unpkg.com/@simplewebauthn/browser/dist/bundle/index.umd.min.js"> <script src="https://unpkg.com/@simplewebauthn/browser/dist/bundle/index.umd.min.js">
</script> </script>
<script src="/public/auth-client.js?v=4"></script> <script src="/public/auth-client.js?v=5"></script>
</head> </head>
<body> <body>
<header class="header"> <header class="header">

View File

@ -95,7 +95,7 @@ export const LoginPage = () => {
</div> </div>
</div> </div>
<script src="/public/auth-client.js?v=4"></script> <script src="/public/auth-client.js?v=5"></script>
<script <script
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: ` __html: `

View File

@ -119,7 +119,7 @@ export const RegisterPage = (
</div> </div>
</div> </div>
<script src="/public/auth-client.js?v=4"></script> <script src="/public/auth-client.js?v=5"></script>
<script <script
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: ` __html: `