feat(auth): integrate authentication with installation lifecycle

This commit is contained in:
2026-08-17 23:33:51 +02:00
parent 0d0c15b4b8
commit e8b9995ed0
21 changed files with 672 additions and 234 deletions
+3 -3
View File
@@ -169,7 +169,7 @@ test("the session boundary exposes only exact health and authentication protocol
expect((await app.inject({ method: "GET", url: "/auth/configured" })).statusCode).toBe(401);
});
test("the session boundary retains the exact loopback tht maintenance identity in configured auth modes", async () => {
test("loopback maintenance headers can never mint an administrator in configured auth modes", async () => {
const app = Fastify();
app.addHook("preHandler", authenticateSession({ mode: "local" }));
app.get("/private", async (request) => getPrincipal(request));
@@ -183,8 +183,8 @@ test("the session boundary retains the exact loopback tht maintenance identity i
for (const method of ["GET", "POST"] as const) {
const response = await app.inject({ method, url: "/private", headers, remoteAddress: "127.0.0.1" });
expect(response.statusCode).toBe(200);
expect(response.json()).toMatchObject({ issuer: "tht", subject: "tht-maintenance", isAdmin: true });
expect(response.statusCode).toBe(503);
expect(response.body).not.toContain("tht-maintenance");
}
});
+34
View File
@@ -1,3 +1,7 @@
import { spawnSync } from "node:child_process";
import { chmodSync, mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { describe, expect, it } from "vitest";
import { formatStartupFailure } from "../src/startup-error.js";
@@ -25,4 +29,34 @@ describe("formatStartupFailure", () => {
expect(formatted).not.toContain(leaked);
}
});
it("sanitizes synchronous configuration failures from the real server subprocess", () => {
const root = mkdtempSync(join(tmpdir(), "thothii-startup-secret-"));
const secret = "startup-password-do-not-log";
const authDirectory = join(root, `auth-${secret}`);
mkdirSync(authDirectory, { mode: 0o700 });
const authFile = join(authDirectory, "auth.yaml");
writeFileSync(authFile, `version: 1\nmode: local\npassword: ${secret}\n`, { mode: 0o600 });
chmodSync(authDirectory, 0o700);
const entrypoint = resolve(process.cwd(), "src/server.ts");
const result = spawnSync(process.execPath, ["--import", "tsx", entrypoint], {
cwd: process.cwd(),
encoding: "utf8",
timeout: 15_000,
env: {
...process.env,
NODE_ENV: "test",
THT_AUTH_CONFIG_FILE: authFile,
THT_AUTH_STATE_ROOT: join(root, "auth-state"),
},
});
expect(result.status).toBe(1);
expect(result.stdout).toBe("");
expect(result.stderr.trim()).toBe("backend startup failed: auth_config_invalid");
expect(`${result.stdout}${result.stderr}`).not.toContain(secret);
expect(`${result.stdout}${result.stderr}`).not.toContain(authDirectory);
expect(`${result.stdout}${result.stderr}`).not.toContain("server.ts");
});
});