auth-core-ai.ts139 lines · main
1/**
2 * AI agent token admin + verify (SuperTokens-class AI auth first cut).
3 *
4 * Dashboard (project admin):
5 * GET/POST /v1/auth-core/projects/:projectId/ai/agents
6 * DELETE /v1/auth-core/projects/:projectId/ai/agents/:tokenId
7 *
8 * Public verify (Bearer brai_…):
9 * GET /v1/auth-core/ai/me
10 */
11
12import { Hono } from 'hono';
13
14import { requireAuthCoreProject } from '../middleware/auth-core-guard.js';
15import { BRIVEN_ENGINE_ID } from '../services/auth-core/engine.js';
16import {
17 createAiAgentToken,
18 listAiAgentTokens,
19 revokeAiAgentToken,
20 verifyAiAgentToken,
21} from '../services/auth-core/ai-auth.js';
22import type { AppEnv } from '../types/app-env.js';
23import type { User } from '../middleware/session.js';
24
25export const authCoreAiRouter = new Hono<AppEnv>();
26
27authCoreAiRouter.use(
28 '/v1/auth-core/projects/:projectId/ai/agents',
29 ...requireAuthCoreProject('admin'),
30);
31authCoreAiRouter.use(
32 '/v1/auth-core/projects/:projectId/ai/agents/*',
33 ...requireAuthCoreProject('admin'),
34);
35
36authCoreAiRouter.get(
37 '/v1/auth-core/projects/:projectId/ai/agents',
38 async (c) => {
39 const projectId = c.req.param('projectId');
40 try {
41 const agents = await listAiAgentTokens(projectId);
42 return c.json({ engine: BRIVEN_ENGINE_ID, projectId, agents });
43 } catch (err) {
44 return c.json(
45 {
46 engine: BRIVEN_ENGINE_ID,
47 code: 'list_failed',
48 message: err instanceof Error ? err.message : String(err),
49 },
50 500,
51 );
52 }
53 },
54);
55
56authCoreAiRouter.post(
57 '/v1/auth-core/projects/:projectId/ai/agents',
58 async (c) => {
59 const projectId = c.req.param('projectId');
60 let body: { agentName?: string; scopes?: string[]; ttlHours?: number } = {};
61 try {
62 body = await c.req.json();
63 } catch {
64 body = {};
65 }
66 const user = c.get('user') as User | null;
67 try {
68 const created = await createAiAgentToken({
69 projectId,
70 agentName: body.agentName ?? 'agent',
71 scopes: body.scopes,
72 ttlHours: body.ttlHours,
73 createdBy: user?.id ?? null,
74 });
75 return c.json({
76 engine: BRIVEN_ENGINE_ID,
77 projectId,
78 agent: created.token,
79 /** Shown once */
80 plaintext: created.plaintext,
81 note: 'Copy the token now — it is not shown again. Use Authorization: Bearer brai_…',
82 });
83 } catch (err) {
84 return c.json(
85 {
86 engine: BRIVEN_ENGINE_ID,
87 code: 'create_failed',
88 message: err instanceof Error ? err.message : String(err),
89 },
90 400,
91 );
92 }
93 },
94);
95
96authCoreAiRouter.delete(
97 '/v1/auth-core/projects/:projectId/ai/agents/:tokenId',
98 async (c) => {
99 const projectId = c.req.param('projectId');
100 const tokenId = c.req.param('tokenId');
101 try {
102 await revokeAiAgentToken(projectId, tokenId);
103 return c.json({ engine: BRIVEN_ENGINE_ID, ok: true, projectId, tokenId });
104 } catch (err) {
105 return c.json(
106 {
107 engine: BRIVEN_ENGINE_ID,
108 code: 'revoke_failed',
109 message: err instanceof Error ? err.message : String(err),
110 },
111 404,
112 );
113 }
114 },
115);
116
117authCoreAiRouter.get('/v1/auth-core/ai/me', async (c) => {
118 const auth = c.req.header('authorization') ?? '';
119 const token = auth.startsWith('Bearer ') ? auth.slice(7).trim() : '';
120 if (!token) {
121 return c.json(
122 { engine: BRIVEN_ENGINE_ID, authenticated: false, code: 'unauthorized' },
123 401,
124 );
125 }
126 const verified = await verifyAiAgentToken(token);
127 if (!verified) {
128 return c.json(
129 { engine: BRIVEN_ENGINE_ID, authenticated: false, code: 'invalid_token' },
130 401,
131 );
132 }
133 return c.json({
134 engine: BRIVEN_ENGINE_ID,
135 authenticated: true,
136 kind: 'ai_agent',
137 ...verified,
138 });
139});