auth-core.ts174 lines · main
1/**
2 * Briven Auth Core public routes (briven-engine on Doltgres).
3 *
4 * - GET /v1/auth-core/info — engine status (no secrets)
5 * - GET /v1/auth-core/ready — health
6 * - GET /v1/auth-core/map/:projectId — project → tenant map
7 * - Legacy multi-tenant Better Auth product paths → 410 Gone
8 * - Enable Auth is bridged to briven-engine (not 410)
9 *
10 * Platform operator login (/v1/auth/* Better Auth for briven.tech) is NOT here.
11 */
12
13import { Hono } from 'hono';
14
15import { requireAuth } from '../middleware/session.js';
16import {
17 requireProjectAuth,
18 requireProjectRole,
19} from '../middleware/project-auth.js';
20import { BUILD_AT, BUILD_SHA } from './health.js';
21import { mapProjectToAuthCore } from '../services/auth-core/project-map.js';
22import {
23 BRIVEN_ENGINE_ID,
24 probeBrivenEngine,
25} from '../services/auth-core/engine.js';
26import {
27 enableBrivenEngineAuth,
28 listBrivenEngineWorkspace,
29} from '../services/auth-core/workspace.js';
30import type { AppEnv } from '../types/app-env.js';
31
32export const authCoreRouter = new Hono<AppEnv>();
33
34const GONE = {
35 code: 'auth_product_retired',
36 message:
37 'That Auth path was retired. Use Briven Auth (briven-engine) under /v1/auth-core/*.',
38 engine: BRIVEN_ENGINE_ID,
39} as const;
40
41authCoreRouter.get('/v1/auth-core/info', async (c) => {
42 const core = await probeBrivenEngine();
43 return c.json({
44 service: 'briven-auth-core',
45 product: 'Briven Auth',
46 engine: BRIVEN_ENGINE_ID,
47 productStatus: 'live-on-briven-engine',
48 buildSha: BUILD_SHA,
49 buildAt: BUILD_AT,
50 ...core,
51 });
52});
53
54authCoreRouter.get('/v1/auth-core/ready', async (c) => {
55 const core = await probeBrivenEngine();
56 if (!core.ok) {
57 return c.json({ status: 'not_ready', ...core }, 503);
58 }
59 return c.json({ status: 'ready', engine: BRIVEN_ENGINE_ID, ...core });
60});
61
62/** Phase 1.4 — projectId → briven-engine appId/tenantId (rule-based until Multitenancy). */
63authCoreRouter.get('/v1/auth-core/map/:projectId', (c) => {
64 const projectId = c.req.param('projectId');
65 try {
66 return c.json(mapProjectToAuthCore(projectId));
67 } catch (err) {
68 return c.json(
69 {
70 code: 'invalid_project_id',
71 message: err instanceof Error ? err.message : String(err),
72 },
73 400,
74 );
75 }
76});
77
78/**
79 * Dashboard workspace — projects + Auth on/off (Doltgres tenants).
80 * Replaces legacy /v1/auth-v2/workspace.
81 */
82authCoreRouter.get(
83 '/v1/auth-core/workspace',
84 requireAuth(),
85 async (c) => {
86 const user = c.get('user');
87 if (!user?.id) {
88 return c.json(
89 { code: 'unauthorized', message: 'sign in required', engine: BRIVEN_ENGINE_ID },
90 401,
91 );
92 }
93 const data = await listBrivenEngineWorkspace(user.id);
94 return c.json({ ok: true, ...data });
95 },
96);
97
98/**
99 * Enable Auth for a project — creates briven-engine tenant on Doltgres.
100 * Also available as POST /v1/projects/:id/auth/enable (bridge below).
101 */
102authCoreRouter.post(
103 '/v1/auth-core/projects/:projectId/enable',
104 ...[requireProjectAuth('projectId'), requireProjectRole('admin')],
105 async (c) => {
106 const projectId = c.req.param('projectId');
107 const result = await enableBrivenEngineAuth(projectId);
108 return c.json(result, result.ok ? 200 : 503);
109 },
110);
111
112/**
113 * Bridge: dashboard "enable Auth" buttons still call the old path.
114 * Do NOT return 410 — wire to briven-engine instead.
115 */
116authCoreRouter.post(
117 '/v1/projects/:id/auth/enable',
118 ...[requireProjectAuth('id'), requireProjectRole('admin')],
119 async (c) => {
120 const projectId = c.req.param('id');
121 const result = await enableBrivenEngineAuth(projectId);
122 if (!result.ok) {
123 return c.json(
124 {
125 code: 'auth_enable_failed',
126 message: result.message ?? 'could not enable Auth on briven-engine',
127 engine: BRIVEN_ENGINE_ID,
128 },
129 503,
130 );
131 }
132 return c.json({
133 ok: true,
134 engine: BRIVEN_ENGINE_ID,
135 projectId: result.projectId,
136 tenantId: result.tenantId,
137 authEnabled: true,
138 created: result.created,
139 message: result.created
140 ? 'Auth enabled for this project'
141 : 'Auth already on for this project',
142 storage: 'doltgres',
143 });
144 },
145);
146
147/**
148 * Bridge: workspace list for older UI that still hits auth-v2.
149 */
150authCoreRouter.get('/v1/auth-v2/workspace', requireAuth(), async (c) => {
151 const user = c.get('user');
152 if (!user?.id) {
153 return c.json({ code: 'unauthorized' }, 401);
154 }
155 const data = await listBrivenEngineWorkspace(user.id);
156 return c.json({ ok: true, engine: 'briven-engine', projects: data.projects });
157});
158
159/** Old multi-tenant Better Auth product — gone (except enable + workspace bridges above). */
160authCoreRouter.all('/v1/auth-tenant/*', (c) => c.json(GONE, 410));
161// Note: GET /v1/auth-v2/workspace is registered above; other auth-v2 paths stay retired.
162authCoreRouter.all('/v1/auth-v2/*', (c) => c.json(GONE, 410));
163// Note: POST /v1/projects/:id/auth/enable is registered above; other project auth paths retired.
164authCoreRouter.all('/v1/projects/:id/auth/*', (c) =>
165 c.json(
166 {
167 ...GONE,
168 message:
169 'Use Briven Auth under /dashboard/auth and /v1/auth-core/* for this action.',
170 },
171 410,
172 ),
173);
174authCoreRouter.all('/v1/projects/:id/scim/*', (c) => c.json(GONE, 410));