import type { Context, Next } from "hono"; import { verifyServiceJwt } from "../service-auth.js"; import { verifyAccessToken, TokenExpiredError } from "../session.js"; import type { Config } from "../config.js"; export interface AuthInfo { did: string; scope: string; } export type AuthVariables = { auth: AuthInfo; }; export async function requireAuth( c: Context<{ Bindings: Config; Variables: AuthVariables }>, next: Next, ): Promise { const auth = c.req.header("Authorization"); if (!auth) { return c.json( { error: "AuthMissing", message: "Authorization header required", }, 401, ); } // Handle Bearer tokens (session JWTs, static token, service JWTs) if (!auth.startsWith("Bearer ")) { return c.json( { error: "AuthMissing", message: "Invalid authorization scheme", }, 401, ); } const token = auth.slice(7); // Try static token first if (token === c.env.AUTH_TOKEN) { c.set("auth", { did: c.env.DID ?? "", scope: "com.atproto.access" }); return next(); } // Legacy JWT auth requires PDS_HOSTNAME and JWT_SECRET if (!c.env.PDS_HOSTNAME || !c.env.JWT_SECRET) { return c.json({ error: "AuthenticationRequired", message: "Not authenticated" }, 401); } const serviceDid = `did:web:${c.env.PDS_HOSTNAME}`; // Try session JWT verification (HS256, signed with JWT_SECRET) try { const payload = await verifyAccessToken( token, c.env.JWT_SECRET, serviceDid, ); if (!payload.sub || payload.sub !== c.env.DID) { return c.json( { error: "AuthenticationRequired", message: "Invalid access token", }, 401, ); } c.set("auth", { did: payload.sub as string, scope: payload.scope as string }); return next(); } catch (err) { if (err instanceof TokenExpiredError) { return c.json( { error: "ExpiredToken", message: err.message, }, 400, ); } // Session JWT verification failed, try service JWT } // Try service JWT verification (ES256K, signed with our signing key) if (c.env.SIGNING_KEY && c.env.DID) { try { const payload = await verifyServiceJwt( token, c.env.SIGNING_KEY, serviceDid, c.env.DID, ); if (payload.iss === c.env.DID) { c.set("auth", { did: payload.iss, scope: payload.lxm || "atproto" }); return next(); } } catch { // Service JWT verification also failed } } return c.json( { error: "AuthenticationRequired", message: "Invalid authentication token", }, 401, ); }