File size: 984 Bytes
416bdd1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { createHash, timingSafeEqual } from "node:crypto";

/**
 * Checks `Authorization: Bearer $ADMIN_TOKEN`. Returns "disabled" when no token is
 * configured so the admin routes can answer 404 and stay invisible.
 */
export function checkAdmin(request: Request): "ok" | "denied" | "disabled" {
  const expected = process.env.ADMIN_TOKEN;
  if (!expected) return "disabled";
  const header = request.headers.get("authorization") ?? "";
  const provided = header.startsWith("Bearer ") ? header.slice(7) : "";
  // Hash both sides so the comparison has equal length and does not leak it.
  const a = createHash("sha256").update(provided).digest();
  const b = createHash("sha256").update(expected).digest();
  return timingSafeEqual(a, b) ? "ok" : "denied";
}

export function adminDenied(result: "denied" | "disabled") {
  return result === "disabled"
    ? Response.json({ error: "Not found." }, { status: 404 })
    : Response.json({ error: "Unauthorized." }, { status: 401 });
}