Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesPull RequestsActionsSecurityInsightsSettings
✨ AI
More
Blame · Line-by-line history

api-auth-2fa.test.ts

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

api-auth-2fa.test.tsBlame51 lines · 1 contributor
81807e1ccantynz-alt1/**
2 * 2FA must hold on the API surface too.
3 *
4 * `sessions.requires_2fa` is set when the password step succeeds and cleared
5 * only once the second factor is accepted (routes/auth.tsx:565, 674, 716).
6 * middleware/auth.ts honours it in both its softAuth and requireAuth paths
7 * (lines ~200 and ~273).
8 *
9 * middleware/api-auth.ts's session-cookie fallback checked only expiry. A
10 * session that had passed the password step but not 2FA therefore
11 * authenticated fully against every route using apiAuth — and was handed
12 * ["repo","user","admin"] scopes — bypassing 2FA for the whole API surface.
13 */
14
15import { describe, expect, it } from "bun:test";
16import { readFileSync } from "fs";
17
18const API_AUTH = readFileSync("src/middleware/api-auth.ts", "utf8");
19const AUTH = readFileSync("src/middleware/auth.ts", "utf8");
20
21describe("api-auth session fallback honours requires2fa", () => {
22 it("rejects a session still pending its second factor", () => {
23 expect(API_AUTH).toContain("!session.requires2fa");
24 });
25
26 it("checks it alongside expiry, in the same guard", () => {
27 // Guards against the check being added somewhere that a later `return`
28 // can skip.
29 const guard = API_AUTH.slice(
30 API_AUTH.indexOf("// Fall back to session cookie"),
31 API_AUTH.indexOf('c.set("authMethod", "session")')
32 );
33 expect(guard).toContain("expiresAt");
34 expect(guard).toContain("!session.requires2fa");
35 });
36
37 it("does not grant scopes before the 2FA check", () => {
38 const scopeIdx = API_AUTH.indexOf('c.set("tokenScopes", ["repo", "user", "admin"])');
39 const checkIdx = API_AUTH.indexOf("!session.requires2fa");
40 expect(checkIdx).toBeGreaterThan(-1);
41 expect(scopeIdx).toBeGreaterThan(checkIdx);
42 });
43});
44
45describe("the two middlewares agree", () => {
46 it("both consult requires2fa", () => {
47 // The bug was a divergence between them; keep them in step.
48 expect(AUTH).toContain("session.requires2fa");
49 expect(API_AUTH).toContain("session.requires2fa");
50 });
51});