auth-cli.ts45 lines · main
| 1 | import { Hono } from 'hono'; |
| 2 | |
| 3 | import { signCliToken } from '../lib/cli-jwt.js'; |
| 4 | import { userRateLimit } from '../middleware/rate-limit.js'; |
| 5 | import { requireAuth } from '../middleware/session.js'; |
| 6 | import { audit, hashIp } from '../services/audit.js'; |
| 7 | import type { AppEnv } from '../types/app-env.js'; |
| 8 | |
| 9 | /** |
| 10 | * POST /v1/auth/cli-token — mints a short-lived JWT bound to the calling |
| 11 | * user that the briven CLI can carry in a `Bearer` header on subsequent |
| 12 | * requests. The route accepts either a dashboard session cookie or an |
| 13 | * existing CLI bearer (requireAuth() handles both), so the CLI can refresh |
| 14 | * its own token without a round-trip through the browser. |
| 15 | * |
| 16 | * Token semantics live in src/lib/cli-jwt.ts (24h TTL, scope=cli, |
| 17 | * issuer=briven-api, audience=briven-cli). Every mint is audited under the |
| 18 | * `cli.token.mint` action so an operator can spot unusual issuance patterns. |
| 19 | */ |
| 20 | export const authCliRouter = new Hono<AppEnv>(); |
| 21 | |
| 22 | // Cap mints at 5 per user per 60s window (RATE_LIMIT_WINDOW_MS in |
| 23 | // services/tiers.ts). A legitimate CLI refresh is a once-a-day event; a |
| 24 | // burst above 5 is either a script bug or token-grinding worth slowing. |
| 25 | authCliRouter.post( |
| 26 | '/v1/auth/cli-token', |
| 27 | requireAuth(), |
| 28 | userRateLimit('cli.token.mint', 5), |
| 29 | async (c) => { |
| 30 | const user = c.get('user'); |
| 31 | if (!user) { |
| 32 | return c.json({ code: 'unauthorized', message: 'authentication required' }, 401); |
| 33 | } |
| 34 | const token = await signCliToken(user.id); |
| 35 | await audit({ |
| 36 | actorId: user.id, |
| 37 | projectId: null, |
| 38 | action: 'cli.token.mint', |
| 39 | ipHash: hashIp(c.req.raw.headers.get('cf-connecting-ip') ?? null), |
| 40 | userAgent: c.req.header('user-agent') ?? null, |
| 41 | metadata: { ttlSeconds: 60 * 60 * 24 }, |
| 42 | }); |
| 43 | return c.json({ token }); |
| 44 | }, |
| 45 | ); |