From 850f4f826b536d913235e174dc07aef74e51bf60 Mon Sep 17 00:00:00 2001 From: benj Date: Fri, 1 May 2026 09:36:21 +0800 Subject: irs 990 doc prarsers and some web stuff --- web/api/src/auth.ts | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 web/api/src/auth.ts (limited to 'web/api/src/auth.ts') diff --git a/web/api/src/auth.ts b/web/api/src/auth.ts new file mode 100644 index 0000000..d22f26d --- /dev/null +++ b/web/api/src/auth.ts @@ -0,0 +1,49 @@ +import { createHash } from 'node:crypto'; +import type { MiddlewareHandler } from 'hono'; + +import { lookupApiKey, type ApiKeyLookup } from './db'; + +export interface AuthVars { + apiKey: ApiKeyLookup; +} + +function sha256Hex(input: string): string { + return createHash('sha256').update(input).digest('hex'); +} + +/** + * Hono middleware: require a valid `Authorization: Bearer ti_...` header. + * On success, attaches the resolved ApiKeyLookup to c.var.apiKey so the + * handler can see the caller's account + plan + key. + */ +export const auth: MiddlewareHandler<{ Variables: AuthVars }> = async ( + c, + next +) => { + const header = + c.req.header('authorization') ?? c.req.header('Authorization'); + if (!header) { + return c.json({ error: 'Missing Authorization header' }, 401); + } + + const match = /^Bearer\s+(\S+)$/i.exec(header); + if (!match) { + return c.json( + { error: 'Authorization header must be `Bearer `' }, + 401 + ); + } + + const token = match[1]!; + if (!token.startsWith('ti_')) { + return c.json({ error: 'Invalid API key format' }, 401); + } + + const key = lookupApiKey(sha256Hex(token)); + if (!key) { + return c.json({ error: 'Invalid or revoked API key' }, 401); + } + + c.set('apiKey', key); + return next(); +}; -- cgit v1.2.3