返回 DeepSeek-Reasonix
auth.ts
1 import type { Context, MiddlewareHandler } from "hono";
2 import type { AppEnv } from "../env";
3 import type { RegistryUser } from "../types";
4 import { ApiError } from "./errors";
5
6 // Resolve identity by asking the account service, forwarding the caller's cookie
7 // and Authorization so both the web session and the CLI bearer resolve. The
8 // registry never holds SESSION_PEPPER; accounts stays the sole identity authority.
9 async function fetchAccountUser(c: Context<AppEnv>): Promise<RegistryUser | null> {
10 const cookie = c.req.header("cookie");
11 const authz = c.req.header("authorization");
12 if (!cookie && !authz) return null;
13
14 const headers: Record<string, string> = {};
15 if (cookie) headers["cookie"] = cookie;
16 if (authz) headers["authorization"] = authz;
17
18 const res = await fetch(`${c.env.ACCOUNTS_ORIGIN}/me`, { headers });
19 if (!res.ok) return null;
20 const body = (await res.json()) as {
21 user?: { id?: number; handle?: string; role?: string; emailVerified?: boolean };
22 };
23 const u = body.user;
24 if (!u || typeof u.id !== "number" || typeof u.handle !== "string") return null;
25 return {
26 id: u.id,
27 handle: u.handle,
28 role: u.role === "admin" ? "admin" : "member",
29 emailVerified: u.emailVerified === true,
30 };
31 }
32
33 // Gate for write routes. Public reads never call this, so list/detail never pay
34 // the account round-trip.
35 export const requireAuth: MiddlewareHandler<AppEnv> = async (c, next) => {
36 const user = await fetchAccountUser(c).catch(() => null);
37 if (!user) throw new ApiError(401, "unauthorized", "Sign in at id.reasonix.io to publish.");
38 c.set("user", user);
39 await next();
40 };
41
42 // Gate for moderation routes: a resolved account with the admin role.
43 export const requireAdmin: MiddlewareHandler<AppEnv> = async (c, next) => {
44 const user = await fetchAccountUser(c).catch(() => null);
45 if (!user) throw new ApiError(401, "unauthorized", "Sign in at id.reasonix.io.");
46 if (user.role !== "admin") throw new ApiError(403, "forbidden", "Admins only.");
47 c.set("user", user);
48 await next();
49 };
50
51 export function currentUser(c: Context<AppEnv>): RegistryUser {
52 return c.get("user");
53 }
54
54 lines TYPESCRIPT