File
Blob: src/worker/middleware/auth.ts
| 1 | import type { MiddlewareHandler } from "hono"; |
| 2 | |
| 3 | import { hasAdminRole } from "@/shared/role"; |
| 4 | import type { AppBindings, AppContext, AppSession } from "@/worker/hono"; |
| 5 | import { HttpError } from "@/worker/http"; |
| 6 | |
| 7 | // The `assertNotBanned` predicate inside `makeAuth`'s `hooks.before` |
| 8 | // fires for every cookie-session route, including this server-side |
| 9 | // `auth.api.getSession()` call. A banned user's session lookup throws |
| 10 | // `APIError(FORBIDDEN, code=BANNED_USER)`; the APIError → HttpError |
| 11 | // conversion in `app.onError` (`worker/index.ts`) reshapes it before |
| 12 | // the client sees the response. |
| 13 | const loadSession = async (c: AppContext, message = "Sign in first."): Promise<AppSession> => { |
| 14 | const session = await c.var.auth.api.getSession({ headers: c.req.raw.headers }); |
| 15 | if (!session) { |
| 16 | throw new HttpError(401, "unauthorized", message); |
| 17 | } |
| 18 | c.set("session", session); |
| 19 | return session; |
| 20 | }; |
| 21 | |
| 22 | export const requireUser: MiddlewareHandler<AppBindings> = async (c, next) => { |
| 23 | await loadSession(c); |
| 24 | await next(); |
| 25 | }; |
| 26 | |
| 27 | export const requireAdmin: MiddlewareHandler<AppBindings> = async (c, next) => { |
| 28 | const session = await loadSession(c, "Sign in required."); |
| 29 | if (!hasAdminRole(session.user.role)) { |
| 30 | throw new HttpError(403, "forbidden", "Admin role required."); |
| 31 | } |
| 32 | await next(); |
| 33 | }; |
| 34 | |
| 35 | // Handlers that depend on a session pull it directly from `c.var.session` |
| 36 | // after `requireUser` / `requireAdmin` has run upstream. The non-null |
| 37 | // assertion is safe at the gated callsite; if a future refactor drops |
| 38 | // the gate, TypeScript flags the missing variable type. |
| 39 | export const requireSession = (c: AppContext): AppSession => { |
| 40 | const session = c.var.session; |
| 41 | if (!session) { |
| 42 | throw new HttpError(401, "unauthorized", "Sign in required."); |
| 43 | } |
| 44 | return session; |
| 45 | }; |