Files
codingetandCodex 31d4636dde
CI / install-and-build (pull_request) Successful in 1m29s
CI / format (pull_request) Successful in 51s
CI / typecheck-source (pull_request) Successful in 41s
CI / typecheck-tests (pull_request) Successful in 39s
CI / test (pull_request) Successful in 51s
CI / lint (pull_request) Successful in 21s
ci: add pull request quality gates
Co-Authored-By: gpt-5.6-terra <noreply@openai.com>
2026-07-22 21:50:29 +00:00

144 lines
4.6 KiB
TypeScript

import { describe, it, before, after } from "node:test";
import assert from "node:assert/strict";
import { createTestDb } from "../../helpers/sqlite.js";
import { createTestServer, type TestServer } from "../../helpers/koa.js";
import { hashPassword } from "../../../src/util/hash.js";
import type { SqliteInterface } from "../../../src/db/sqlite/SqliteInterface.js";
const EMAIL = "auth-http@test.example";
const PASSWORD = "auth-http-password";
function getCookie(res: Response, name: string): string | undefined {
const raw = res.headers.getSetCookie?.() ?? [];
for (const entry of raw) {
const [pair] = entry.split(";");
const [key, value] = pair.split("=");
if (key === name) return value;
}
return undefined;
}
describe("api backend: auth over HTTP", async () => {
let db: SqliteInterface;
let closeDb: () => void;
let server: TestServer;
let uid: string;
before(async () => {
({ db, close: closeDb } = await createTestDb());
server = await createTestServer(db);
const pw = await hashPassword(PASSWORD);
const user = await db.createUser({
email: EMAIL,
name: "Auth HTTP User",
password: pw,
flags: {},
});
uid = user.uid;
});
after(async () => {
await server.close();
closeDb();
});
it("POST /auth/login sets a session cookie", async () => {
const res = await fetch(`${server.url}/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: EMAIL, password: PASSWORD }),
});
assert.equal(res.status, 200);
const cookie = getCookie(res, "abode_session");
assert.ok(cookie, "session cookie set");
});
it("session cookie authenticates GET /auth/self", async () => {
const login = await fetch(`${server.url}/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: EMAIL, password: PASSWORD }),
});
const cookie = getCookie(login, "abode_session");
const self = await fetch(`${server.url}/auth/self`, {
headers: { Cookie: `abode_session=${cookie}` },
});
assert.equal(self.status, 200);
const body = (await self.json()) as { uid: string };
assert.equal(body.uid, uid);
});
it("GET /auth/self without credentials returns 401", async () => {
const res = await fetch(`${server.url}/auth/self`);
assert.equal(res.status, 401);
});
it("POST /auth/logout clears the cookie and invalidates the session server-side", async () => {
const login = await fetch(`${server.url}/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: EMAIL, password: PASSWORD }),
});
const cookie = getCookie(login, "abode_session");
const logout = await fetch(`${server.url}/auth/logout`, {
method: "POST",
headers: { Cookie: `abode_session=${cookie}` },
});
assert.equal(logout.status, 204);
assert.equal(getCookie(logout, "abode_session"), "");
const self = await fetch(`${server.url}/auth/self`, {
headers: { Cookie: `abode_session=${cookie}` },
});
assert.equal(
self.status,
401,
"session was invalidated server-side, not just the cookie cleared",
);
});
it("POST /auth/clear-sessions invalidates outstanding session cookies", async () => {
const login = await fetch(`${server.url}/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: EMAIL, password: PASSWORD }),
});
const cookie = getCookie(login, "abode_session");
const clear = await fetch(`${server.url}/auth/clear-sessions`, {
method: "POST",
headers: { Cookie: `abode_session=${cookie}` },
});
assert.equal(clear.status, 204);
const self = await fetch(`${server.url}/auth/self`, {
headers: { Cookie: `abode_session=${cookie}` },
});
assert.equal(self.status, 401);
});
it("Bearer apikey token authenticates protected routes", async () => {
const [, token] = await db.createApikey({
uid,
name: "HTTP Test Key",
permissions: { all: true },
});
const res = await fetch(`${server.url}/users`, {
headers: { Authorization: `Bearer ${token}` },
});
assert.equal(res.status, 200);
});
it("invalid Bearer apikey token returns 401 invalid_apikey", async () => {
const res = await fetch(`${server.url}/users`, {
headers: { Authorization: `Bearer at_${"0".repeat(32)}` },
});
assert.equal(res.status, 401);
const body = (await res.json()) as { error: string };
assert.equal(body.error, "invalid_apikey");
});
});