Skip to content
File

Blob: src/worker/middleware/admin-allowlist.ts

typescript36 lines
1import type { MiddlewareHandler } from "hono";
2 
3import type { AppBindings } from "@/worker/hono";
4 
5// Better Auth's `admin` plugin exposes a wide surface (create-user,
6// set-user-password, impersonate-user, remove-user, session revocation, ...)
7// that tessera does not use. tessera registration is invite-only, so a
8// signed-in admin should not be able to bypass invites by hitting the raw
9// /api/auth/admin/create-user endpoint just because it remains mounted.
10//
11// Allowlist the routes the tessera admin UI actually drives, keyed on
12// (path, method). Everything else under /api/auth/admin/* returns 404
13// before reaching Better Auth, including wrong-method calls on listed
14// paths (POST /admin/list-users, GET /admin/set-role).
15//
16// `/api/auth/admin/ban-user` is intentionally excluded here because it
17// is served by the tessera handler in `api/admin/users.ts` (mounted
18// before this middleware runs in `worker/index.ts`); the cleanup needs
19// to happen atomically with the ban flag flip.
20type Method = "GET" | "POST";
21 
22const ADMIN_ALLOWLIST: ReadonlyMap<string, Method> = new Map([
23 ["/api/auth/admin/list-users", "GET"],
24 ["/api/auth/admin/set-role", "POST"],
25 ["/api/auth/admin/unban-user", "POST"],
26]);
27 
28export const adminRouteAllowlist: MiddlewareHandler<AppBindings> = async (c, next) => {
29 const allowedMethod = ADMIN_ALLOWLIST.get(c.req.path);
30 if (allowedMethod && c.req.method === allowedMethod) {
31 await next();
32 return;
33 }
34 return new Response("Not Found", { status: 404 });
35};