@agentuity/auth 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agentuity/config.d.ts +160 -150
- package/dist/agentuity/config.d.ts.map +1 -1
- package/dist/agentuity/config.js +1 -1
- package/dist/agentuity/config.js.map +1 -1
- package/dist/agentuity/react.d.ts +34 -34
- package/package.json +28 -8
- package/AGENTS.md +0 -117
- package/src/agentuity/config.ts +0 -401
- package/src/agentuity/plugins/api-key.ts +0 -158
- package/src/agentuity/plugins/index.ts +0 -35
- package/src/agentuity/plugins/jwt.ts +0 -30
- package/src/agentuity/plugins/organization.ts +0 -345
- package/src/agentuity/react.tsx +0 -366
- package/src/agentuity/server.ts +0 -734
- package/src/agentuity/types.ts +0 -201
- package/src/index.ts +0 -86
- package/src/schema.ts +0 -270
- package/src/types.ts +0 -30
- package/test/agentuity/config.test.ts +0 -621
- package/test/agentuity/server.test.ts +0 -537
- package/test/schema.test.ts +0 -147
- package/tsconfig.json +0 -13
- package/tsconfig.test.json +0 -11
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -1,537 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { describe, test, expect, mock } from 'bun:test';
|
|
3
|
-
import { Hono } from 'hono';
|
|
4
|
-
import { createSessionMiddleware, mountAuthRoutes } from '../../src/agentuity/server';
|
|
5
|
-
|
|
6
|
-
const createMockAuth = (sessionResult: unknown) => ({
|
|
7
|
-
api: {
|
|
8
|
-
getSession: mock(() => Promise.resolve(sessionResult)),
|
|
9
|
-
},
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
describe('Agentuity Auth server middleware', () => {
|
|
13
|
-
test('returns 401 when session is null', async () => {
|
|
14
|
-
const mockAuth = createMockAuth(null);
|
|
15
|
-
const app = new Hono();
|
|
16
|
-
|
|
17
|
-
app.use('/protected', createSessionMiddleware(mockAuth as any));
|
|
18
|
-
app.get('/protected', (c) => c.json({ success: true }));
|
|
19
|
-
|
|
20
|
-
const res = await app.request('/protected', {
|
|
21
|
-
method: 'GET',
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
expect(res.status).toBe(401);
|
|
25
|
-
const body = await res.json();
|
|
26
|
-
expect(body).toEqual({ error: 'Unauthorized' });
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
test('returns 401 when getSession throws', async () => {
|
|
30
|
-
const mockAuth = {
|
|
31
|
-
api: {
|
|
32
|
-
getSession: mock(() => Promise.reject(new Error('Session error'))),
|
|
33
|
-
},
|
|
34
|
-
};
|
|
35
|
-
const app = new Hono();
|
|
36
|
-
|
|
37
|
-
app.use('/protected', createSessionMiddleware(mockAuth as any));
|
|
38
|
-
app.get('/protected', (c) => c.json({ success: true }));
|
|
39
|
-
|
|
40
|
-
const res = await app.request('/protected', {
|
|
41
|
-
method: 'GET',
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
expect(res.status).toBe(401);
|
|
45
|
-
const body = await res.json();
|
|
46
|
-
expect(body).toEqual({ error: 'Unauthorized' });
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
test('allows request and attaches auth when session is valid', async () => {
|
|
50
|
-
const mockSession = {
|
|
51
|
-
user: {
|
|
52
|
-
id: 'user_123',
|
|
53
|
-
name: 'Test User',
|
|
54
|
-
email: 'test@example.com',
|
|
55
|
-
},
|
|
56
|
-
session: {
|
|
57
|
-
id: 'session_456',
|
|
58
|
-
token: 'abc123',
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
const mockAuth = createMockAuth(mockSession);
|
|
62
|
-
const app = new Hono();
|
|
63
|
-
|
|
64
|
-
app.use('/protected', createSessionMiddleware(mockAuth as any));
|
|
65
|
-
app.get('/protected', async (c) => {
|
|
66
|
-
const user = await c.var.auth.getUser();
|
|
67
|
-
return c.json({
|
|
68
|
-
success: true,
|
|
69
|
-
userId: user.id,
|
|
70
|
-
userName: user.name,
|
|
71
|
-
userEmail: user.email,
|
|
72
|
-
});
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
const res = await app.request('/protected', {
|
|
76
|
-
method: 'GET',
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
expect(res.status).toBe(200);
|
|
80
|
-
const body = await res.json();
|
|
81
|
-
expect(body).toEqual({
|
|
82
|
-
success: true,
|
|
83
|
-
userId: 'user_123',
|
|
84
|
-
userName: 'Test User',
|
|
85
|
-
userEmail: 'test@example.com',
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
test('caches user on multiple getUser calls', async () => {
|
|
90
|
-
const mockSession = {
|
|
91
|
-
user: { id: 'user_123', name: 'Test', email: 'test@example.com' },
|
|
92
|
-
session: { id: 'session_456' },
|
|
93
|
-
};
|
|
94
|
-
const mockAuth = createMockAuth(mockSession);
|
|
95
|
-
const app = new Hono();
|
|
96
|
-
|
|
97
|
-
app.use('/protected', createSessionMiddleware(mockAuth as any));
|
|
98
|
-
app.get('/protected', async (c) => {
|
|
99
|
-
const user1 = await c.var.auth.getUser();
|
|
100
|
-
const user2 = await c.var.auth.getUser();
|
|
101
|
-
return c.json({
|
|
102
|
-
same: user1 === user2,
|
|
103
|
-
userId: user1.id,
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
const res = await app.request('/protected');
|
|
108
|
-
const body = await res.json();
|
|
109
|
-
expect(body.same).toBe(true);
|
|
110
|
-
expect(body.userId).toBe('user_123');
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
test('getToken returns bearer token from Authorization header', async () => {
|
|
114
|
-
const mockSession = {
|
|
115
|
-
user: { id: 'user_123' },
|
|
116
|
-
session: { id: 'session_456' },
|
|
117
|
-
};
|
|
118
|
-
const mockAuth = createMockAuth(mockSession);
|
|
119
|
-
const app = new Hono();
|
|
120
|
-
|
|
121
|
-
app.use('/protected', createSessionMiddleware(mockAuth as any));
|
|
122
|
-
app.get('/protected', async (c) => {
|
|
123
|
-
const token = await c.var.auth.getToken();
|
|
124
|
-
return c.json({ token });
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
const res = await app.request('/protected', {
|
|
128
|
-
headers: { Authorization: 'Bearer my-token-123' },
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
const body = await res.json();
|
|
132
|
-
expect(body.token).toBe('my-token-123');
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
test('getToken returns null when no Authorization header', async () => {
|
|
136
|
-
const mockSession = {
|
|
137
|
-
user: { id: 'user_123' },
|
|
138
|
-
session: { id: 'session_456' },
|
|
139
|
-
};
|
|
140
|
-
const mockAuth = createMockAuth(mockSession);
|
|
141
|
-
const app = new Hono();
|
|
142
|
-
|
|
143
|
-
app.use('/protected', createSessionMiddleware(mockAuth as any));
|
|
144
|
-
app.get('/protected', async (c) => {
|
|
145
|
-
const token = await c.var.auth.getToken();
|
|
146
|
-
return c.json({ token });
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
const res = await app.request('/protected');
|
|
150
|
-
const body = await res.json();
|
|
151
|
-
expect(body.token).toBe(null);
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
test('exposes raw session on auth.raw', async () => {
|
|
155
|
-
const mockSession = {
|
|
156
|
-
user: { id: 'user_123', customField: 'custom-value' },
|
|
157
|
-
session: { id: 'session_456', expiresAt: '2024-12-31' },
|
|
158
|
-
};
|
|
159
|
-
const mockAuth = createMockAuth(mockSession);
|
|
160
|
-
const app = new Hono();
|
|
161
|
-
|
|
162
|
-
app.use('/protected', createSessionMiddleware(mockAuth as any));
|
|
163
|
-
app.get('/protected', async (c) => {
|
|
164
|
-
return c.json({
|
|
165
|
-
userId: c.var.auth.raw.user.id,
|
|
166
|
-
sessionId: c.var.auth.raw.session.id,
|
|
167
|
-
customField: c.var.auth.raw.user.customField,
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
const res = await app.request('/protected');
|
|
172
|
-
const body = await res.json();
|
|
173
|
-
expect(body).toEqual({
|
|
174
|
-
userId: 'user_123',
|
|
175
|
-
sessionId: 'session_456',
|
|
176
|
-
customField: 'custom-value',
|
|
177
|
-
});
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
describe('optional mode', () => {
|
|
181
|
-
test('allows unauthenticated requests when optional=true', async () => {
|
|
182
|
-
const mockAuth = createMockAuth(null);
|
|
183
|
-
const app = new Hono();
|
|
184
|
-
|
|
185
|
-
app.use('/public', createSessionMiddleware(mockAuth as any, { optional: true }));
|
|
186
|
-
app.get('/public', (c) => {
|
|
187
|
-
return c.json({
|
|
188
|
-
hasAuth: c.var.auth !== undefined,
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
const res = await app.request('/public');
|
|
193
|
-
expect(res.status).toBe(200);
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
test('attaches auth when session exists in optional mode', async () => {
|
|
197
|
-
const mockSession = {
|
|
198
|
-
user: { id: 'user_123' },
|
|
199
|
-
session: { id: 'session_456' },
|
|
200
|
-
};
|
|
201
|
-
const mockAuth = createMockAuth(mockSession);
|
|
202
|
-
const app = new Hono();
|
|
203
|
-
|
|
204
|
-
app.use('/public', createSessionMiddleware(mockAuth as any, { optional: true }));
|
|
205
|
-
app.get('/public', async (c) => {
|
|
206
|
-
const user = await c.var.auth.getUser();
|
|
207
|
-
return c.json({ userId: user.id });
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
const res = await app.request('/public');
|
|
211
|
-
const body = await res.json();
|
|
212
|
-
expect(body.userId).toBe('user_123');
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
test('sets user and session to null when unauthenticated in optional mode', async () => {
|
|
216
|
-
const mockAuth = createMockAuth(null);
|
|
217
|
-
const app = new Hono();
|
|
218
|
-
|
|
219
|
-
app.use('/public', createSessionMiddleware(mockAuth as any, { optional: true }));
|
|
220
|
-
app.get('/public', (c) => {
|
|
221
|
-
return c.json({
|
|
222
|
-
user: c.var.user,
|
|
223
|
-
authSession: c.var.authSession,
|
|
224
|
-
});
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
const res = await app.request('/public');
|
|
228
|
-
const body = await res.json();
|
|
229
|
-
expect(body.user).toBe(null);
|
|
230
|
-
expect(body.authSession).toBe(null);
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
describe('Agentuity Auth pattern (user/session on context)', () => {
|
|
235
|
-
test('sets user and session directly on context', async () => {
|
|
236
|
-
const mockSession = {
|
|
237
|
-
user: { id: 'user_123', name: 'Test', email: 'test@example.com' },
|
|
238
|
-
session: { id: 'session_456', token: 'abc' },
|
|
239
|
-
};
|
|
240
|
-
const mockAuth = createMockAuth(mockSession);
|
|
241
|
-
const app = new Hono();
|
|
242
|
-
|
|
243
|
-
app.use('/api', createSessionMiddleware(mockAuth as any));
|
|
244
|
-
app.get('/api', (c) => {
|
|
245
|
-
return c.json({
|
|
246
|
-
userId: (c.var.user as any)?.id,
|
|
247
|
-
sessionId: (c.var.authSession as any)?.id,
|
|
248
|
-
});
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
const res = await app.request('/api');
|
|
252
|
-
const body = await res.json();
|
|
253
|
-
expect(body.userId).toBe('user_123');
|
|
254
|
-
expect(body.sessionId).toBe('session_456');
|
|
255
|
-
});
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
describe('organization enrichment', () => {
|
|
259
|
-
test('provides minimal org from session and fetches full org lazily', async () => {
|
|
260
|
-
const mockSession = {
|
|
261
|
-
user: { id: 'user_123', name: 'Test' },
|
|
262
|
-
session: {
|
|
263
|
-
id: 'session_456',
|
|
264
|
-
activeOrganizationId: 'org_789',
|
|
265
|
-
},
|
|
266
|
-
};
|
|
267
|
-
const mockFullOrg = {
|
|
268
|
-
id: 'org_789',
|
|
269
|
-
name: 'Test Org',
|
|
270
|
-
slug: 'test-org',
|
|
271
|
-
metadata: null,
|
|
272
|
-
members: [{ userId: 'user_123', role: 'admin', id: 'member_123' }],
|
|
273
|
-
};
|
|
274
|
-
const mockAuth = {
|
|
275
|
-
api: {
|
|
276
|
-
getSession: mock(() => Promise.resolve(mockSession)),
|
|
277
|
-
getFullOrganization: mock(() => Promise.resolve(mockFullOrg)),
|
|
278
|
-
},
|
|
279
|
-
};
|
|
280
|
-
const app = new Hono();
|
|
281
|
-
|
|
282
|
-
app.use('/api', createSessionMiddleware(mockAuth as any));
|
|
283
|
-
app.get('/api', async (c) => {
|
|
284
|
-
const user = c.var.user as any;
|
|
285
|
-
const minimalOrg = c.var.org;
|
|
286
|
-
const fullOrg = await c.var.auth.getOrg();
|
|
287
|
-
return c.json({
|
|
288
|
-
userId: user?.id,
|
|
289
|
-
minimalOrgId: minimalOrg?.id,
|
|
290
|
-
minimalOrgName: minimalOrg?.name,
|
|
291
|
-
fullOrgId: fullOrg?.id,
|
|
292
|
-
fullOrgName: fullOrg?.name,
|
|
293
|
-
fullOrgSlug: fullOrg?.slug,
|
|
294
|
-
fullOrgRole: fullOrg?.role,
|
|
295
|
-
});
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
const res = await app.request('/api');
|
|
299
|
-
const body = await res.json();
|
|
300
|
-
expect(body.userId).toBe('user_123');
|
|
301
|
-
expect(body.minimalOrgId).toBe('org_789');
|
|
302
|
-
expect(body.minimalOrgName).toBeUndefined();
|
|
303
|
-
expect(body.fullOrgId).toBe('org_789');
|
|
304
|
-
expect(body.fullOrgName).toBe('Test Org');
|
|
305
|
-
expect(body.fullOrgSlug).toBe('test-org');
|
|
306
|
-
expect(body.fullOrgRole).toBe('admin');
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
test('returns null org when no activeOrganizationId in session', async () => {
|
|
310
|
-
const mockSession = {
|
|
311
|
-
user: { id: 'user_123' },
|
|
312
|
-
session: { id: 'session_456' },
|
|
313
|
-
};
|
|
314
|
-
const mockAuth = createMockAuth(mockSession);
|
|
315
|
-
const app = new Hono();
|
|
316
|
-
|
|
317
|
-
app.use('/api', createSessionMiddleware(mockAuth as any));
|
|
318
|
-
app.get('/api', (c) => {
|
|
319
|
-
const user = c.var.user as any;
|
|
320
|
-
return c.json({
|
|
321
|
-
userId: user?.id,
|
|
322
|
-
hasOrg: c.var.org !== null,
|
|
323
|
-
});
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
const res = await app.request('/api');
|
|
327
|
-
expect(res.status).toBe(200);
|
|
328
|
-
const body = await res.json();
|
|
329
|
-
expect(body.userId).toBe('user_123');
|
|
330
|
-
expect(body.hasOrg).toBe(false);
|
|
331
|
-
});
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
describe('API key detection', () => {
|
|
335
|
-
test('detects x-api-key header', async () => {
|
|
336
|
-
const mockSession = {
|
|
337
|
-
user: { id: 'user_123' },
|
|
338
|
-
session: { id: 'session_456' },
|
|
339
|
-
};
|
|
340
|
-
const mockAuth = createMockAuth(mockSession);
|
|
341
|
-
const app = new Hono();
|
|
342
|
-
|
|
343
|
-
app.use('/api', createSessionMiddleware(mockAuth as any));
|
|
344
|
-
app.get('/api', (c) => c.json({ success: true }));
|
|
345
|
-
|
|
346
|
-
const res = await app.request('/api', {
|
|
347
|
-
headers: { 'x-api-key': 'ak_test_123' },
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
expect(res.status).toBe(200);
|
|
351
|
-
});
|
|
352
|
-
|
|
353
|
-
test('detects Authorization: ApiKey header', async () => {
|
|
354
|
-
const mockSession = {
|
|
355
|
-
user: { id: 'user_123' },
|
|
356
|
-
session: { id: 'session_456' },
|
|
357
|
-
};
|
|
358
|
-
const mockAuth = createMockAuth(mockSession);
|
|
359
|
-
const app = new Hono();
|
|
360
|
-
|
|
361
|
-
app.use('/api', createSessionMiddleware(mockAuth as any));
|
|
362
|
-
app.get('/api', (c) => c.json({ success: true }));
|
|
363
|
-
|
|
364
|
-
const res = await app.request('/api', {
|
|
365
|
-
headers: { Authorization: 'ApiKey ak_test_123' },
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
expect(res.status).toBe(200);
|
|
369
|
-
});
|
|
370
|
-
});
|
|
371
|
-
});
|
|
372
|
-
|
|
373
|
-
describe('mountAuthRoutes', () => {
|
|
374
|
-
test('forwards requests to auth handler', async () => {
|
|
375
|
-
const mockHandler = mock(async (req: Request) => {
|
|
376
|
-
return new Response(JSON.stringify({ path: new URL(req.url).pathname }), {
|
|
377
|
-
status: 200,
|
|
378
|
-
headers: { 'Content-Type': 'application/json' },
|
|
379
|
-
});
|
|
380
|
-
});
|
|
381
|
-
const mockAuth = { handler: mockHandler };
|
|
382
|
-
const app = new Hono();
|
|
383
|
-
|
|
384
|
-
app.on(['GET', 'POST'], '/auth/*', mountAuthRoutes(mockAuth as any));
|
|
385
|
-
|
|
386
|
-
const res = await app.request('/auth/session');
|
|
387
|
-
expect(res.status).toBe(200);
|
|
388
|
-
expect(mockHandler).toHaveBeenCalled();
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
test('preserves Set-Cookie headers from auth handler', async () => {
|
|
392
|
-
const mockHandler = mock(async () => {
|
|
393
|
-
const headers = new Headers();
|
|
394
|
-
headers.append('Set-Cookie', 'session=abc123; Path=/; HttpOnly');
|
|
395
|
-
headers.append('Set-Cookie', 'csrf=xyz789; Path=/');
|
|
396
|
-
return new Response('{"ok": true}', {
|
|
397
|
-
status: 200,
|
|
398
|
-
headers,
|
|
399
|
-
});
|
|
400
|
-
});
|
|
401
|
-
const mockAuth = { handler: mockHandler };
|
|
402
|
-
const app = new Hono();
|
|
403
|
-
|
|
404
|
-
app.on(['GET', 'POST'], '/auth/*', mountAuthRoutes(mockAuth as any));
|
|
405
|
-
|
|
406
|
-
const res = await app.request('/auth/sign-in', { method: 'POST' });
|
|
407
|
-
expect(res.status).toBe(200);
|
|
408
|
-
|
|
409
|
-
// Note: HappyDOM (used for React tests) doesn't properly handle Set-Cookie headers.
|
|
410
|
-
// Skip this assertion in HappyDOM environment.
|
|
411
|
-
const isHappyDOM = typeof (globalThis as any).happyDOM !== 'undefined';
|
|
412
|
-
if (!isHappyDOM) {
|
|
413
|
-
const cookies = res.headers.getSetCookie();
|
|
414
|
-
expect(cookies.length).toBeGreaterThanOrEqual(1);
|
|
415
|
-
}
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
test('returns response body from auth handler', async () => {
|
|
419
|
-
const mockHandler = mock(async () => {
|
|
420
|
-
return new Response(JSON.stringify({ user: { id: 'user_123' } }), {
|
|
421
|
-
status: 200,
|
|
422
|
-
headers: { 'Content-Type': 'application/json' },
|
|
423
|
-
});
|
|
424
|
-
});
|
|
425
|
-
const mockAuth = { handler: mockHandler };
|
|
426
|
-
const app = new Hono();
|
|
427
|
-
|
|
428
|
-
app.on(['GET', 'POST'], '/auth/*', mountAuthRoutes(mockAuth as any));
|
|
429
|
-
|
|
430
|
-
const res = await app.request('/auth/session');
|
|
431
|
-
const body = await res.json();
|
|
432
|
-
expect(body).toEqual({ user: { id: 'user_123' } });
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
test('preserves status code from auth handler', async () => {
|
|
436
|
-
const mockHandler = mock(async () => {
|
|
437
|
-
return new Response('{"error": "Unauthorized"}', {
|
|
438
|
-
status: 401,
|
|
439
|
-
headers: { 'Content-Type': 'application/json' },
|
|
440
|
-
});
|
|
441
|
-
});
|
|
442
|
-
const mockAuth = { handler: mockHandler };
|
|
443
|
-
const app = new Hono();
|
|
444
|
-
|
|
445
|
-
app.on(['GET', 'POST'], '/auth/*', mountAuthRoutes(mockAuth as any));
|
|
446
|
-
|
|
447
|
-
const res = await app.request('/auth/session');
|
|
448
|
-
expect(res.status).toBe(401);
|
|
449
|
-
});
|
|
450
|
-
|
|
451
|
-
test('merges headers set by Hono middleware', async () => {
|
|
452
|
-
const mockHandler = mock(async () => {
|
|
453
|
-
return new Response('{"ok": true}', {
|
|
454
|
-
status: 200,
|
|
455
|
-
headers: {
|
|
456
|
-
'Content-Type': 'application/json',
|
|
457
|
-
'X-Auth-Header': 'from-auth',
|
|
458
|
-
},
|
|
459
|
-
});
|
|
460
|
-
});
|
|
461
|
-
const mockAuth = { handler: mockHandler };
|
|
462
|
-
const app = new Hono();
|
|
463
|
-
|
|
464
|
-
app.use('/auth/*', async (c, next) => {
|
|
465
|
-
c.header('X-Middleware-Header', 'from-middleware');
|
|
466
|
-
await next();
|
|
467
|
-
});
|
|
468
|
-
app.on(['GET', 'POST'], '/auth/*', mountAuthRoutes(mockAuth as any));
|
|
469
|
-
|
|
470
|
-
const res = await app.request('/auth/session');
|
|
471
|
-
// X-Auth-Header is NOT forwarded because it's not in the default allowlist
|
|
472
|
-
expect(res.headers.get('X-Auth-Header')).toBeNull();
|
|
473
|
-
// Content-Type IS forwarded because it's in the allowlist
|
|
474
|
-
expect(res.headers.get('Content-Type')).toBe('application/json');
|
|
475
|
-
// Middleware headers are preserved
|
|
476
|
-
expect(res.headers.get('X-Middleware-Header')).toBe('from-middleware');
|
|
477
|
-
});
|
|
478
|
-
|
|
479
|
-
test('filters out non-allowlisted headers', async () => {
|
|
480
|
-
const mockHandler = mock(async () => {
|
|
481
|
-
// Use Headers API for Set-Cookie to ensure proper multi-value handling
|
|
482
|
-
const headers = new Headers();
|
|
483
|
-
headers.set('Content-Type', 'application/json');
|
|
484
|
-
headers.set('Set-Cookie', 'session=abc123');
|
|
485
|
-
headers.set('Location', '/redirect');
|
|
486
|
-
headers.set('X-Internal-Debug', 'should-not-forward');
|
|
487
|
-
headers.set('Server', 'should-not-forward');
|
|
488
|
-
return new Response('{"ok": true}', { status: 200, headers });
|
|
489
|
-
});
|
|
490
|
-
const mockAuth = { handler: mockHandler };
|
|
491
|
-
const app = new Hono();
|
|
492
|
-
app.on(['GET', 'POST'], '/auth/*', mountAuthRoutes(mockAuth as any));
|
|
493
|
-
|
|
494
|
-
const res = await app.request('/auth/session');
|
|
495
|
-
|
|
496
|
-
// Allowlisted headers are forwarded
|
|
497
|
-
expect(res.headers.get('Content-Type')).toBe('application/json');
|
|
498
|
-
expect(res.headers.get('Location')).toBe('/redirect');
|
|
499
|
-
|
|
500
|
-
// Set-Cookie test: HappyDOM (used for React tests) doesn't properly handle Set-Cookie
|
|
501
|
-
// In native Bun, this works correctly. Skip the assertion if in HappyDOM.
|
|
502
|
-
const isHappyDOM = typeof (globalThis as any).happyDOM !== 'undefined';
|
|
503
|
-
if (!isHappyDOM) {
|
|
504
|
-
expect(res.headers.get('Set-Cookie')).toBe('session=abc123');
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
// Non-allowlisted headers are filtered out
|
|
508
|
-
expect(res.headers.get('X-Internal-Debug')).toBeNull();
|
|
509
|
-
expect(res.headers.get('Server')).toBeNull();
|
|
510
|
-
});
|
|
511
|
-
|
|
512
|
-
test('allows custom header allowlist', async () => {
|
|
513
|
-
const mockHandler = mock(async () => {
|
|
514
|
-
return new Response('{"ok": true}', {
|
|
515
|
-
status: 200,
|
|
516
|
-
headers: {
|
|
517
|
-
'Content-Type': 'application/json',
|
|
518
|
-
'X-Custom-Header': 'custom-value',
|
|
519
|
-
},
|
|
520
|
-
});
|
|
521
|
-
});
|
|
522
|
-
const mockAuth = { handler: mockHandler };
|
|
523
|
-
const app = new Hono();
|
|
524
|
-
app.on(
|
|
525
|
-
['GET', 'POST'],
|
|
526
|
-
'/auth/*',
|
|
527
|
-
mountAuthRoutes(mockAuth as any, {
|
|
528
|
-
allowList: ['content-type', 'x-custom-header'],
|
|
529
|
-
})
|
|
530
|
-
);
|
|
531
|
-
|
|
532
|
-
const res = await app.request('/auth/session');
|
|
533
|
-
|
|
534
|
-
expect(res.headers.get('Content-Type')).toBe('application/json');
|
|
535
|
-
expect(res.headers.get('X-Custom-Header')).toBe('custom-value');
|
|
536
|
-
});
|
|
537
|
-
});
|
package/test/schema.test.ts
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'bun:test';
|
|
2
|
-
import { getTableName } from 'drizzle-orm';
|
|
3
|
-
import * as schema from '../src/schema';
|
|
4
|
-
|
|
5
|
-
describe('Agentuity Auth Schema', () => {
|
|
6
|
-
describe('table exports', () => {
|
|
7
|
-
it('exports user table', () => {
|
|
8
|
-
expect(schema.user).toBeDefined();
|
|
9
|
-
expect(getTableName(schema.user)).toBe('user');
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it('exports session table', () => {
|
|
13
|
-
expect(schema.session).toBeDefined();
|
|
14
|
-
expect(getTableName(schema.session)).toBe('session');
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('exports account table', () => {
|
|
18
|
-
expect(schema.account).toBeDefined();
|
|
19
|
-
expect(getTableName(schema.account)).toBe('account');
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('exports verification table', () => {
|
|
23
|
-
expect(schema.verification).toBeDefined();
|
|
24
|
-
expect(getTableName(schema.verification)).toBe('verification');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('exports organization table', () => {
|
|
28
|
-
expect(schema.organization).toBeDefined();
|
|
29
|
-
expect(getTableName(schema.organization)).toBe('organization');
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('exports member table', () => {
|
|
33
|
-
expect(schema.member).toBeDefined();
|
|
34
|
-
expect(getTableName(schema.member)).toBe('member');
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('exports invitation table', () => {
|
|
38
|
-
expect(schema.invitation).toBeDefined();
|
|
39
|
-
expect(getTableName(schema.invitation)).toBe('invitation');
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('exports jwks table', () => {
|
|
43
|
-
expect(schema.jwks).toBeDefined();
|
|
44
|
-
expect(getTableName(schema.jwks)).toBe('jwks');
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('exports apikey table', () => {
|
|
48
|
-
expect(schema.apikey).toBeDefined();
|
|
49
|
-
expect(getTableName(schema.apikey)).toBe('apikey');
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
describe('relation exports', () => {
|
|
54
|
-
it('exports userRelations', () => {
|
|
55
|
-
expect(schema.userRelations).toBeDefined();
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('exports sessionRelations', () => {
|
|
59
|
-
expect(schema.sessionRelations).toBeDefined();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('exports accountRelations', () => {
|
|
63
|
-
expect(schema.accountRelations).toBeDefined();
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('exports organizationRelations', () => {
|
|
67
|
-
expect(schema.organizationRelations).toBeDefined();
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('exports memberRelations', () => {
|
|
71
|
-
expect(schema.memberRelations).toBeDefined();
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('exports invitationRelations', () => {
|
|
75
|
-
expect(schema.invitationRelations).toBeDefined();
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('exports apikeyRelations', () => {
|
|
79
|
-
expect(schema.apikeyRelations).toBeDefined();
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
describe('combined schema export', () => {
|
|
84
|
-
it('exports authSchema object with all tables and relations', () => {
|
|
85
|
-
expect(schema.authSchema).toBeDefined();
|
|
86
|
-
expect(schema.authSchema.user).toBe(schema.user);
|
|
87
|
-
expect(schema.authSchema.session).toBe(schema.session);
|
|
88
|
-
expect(schema.authSchema.account).toBe(schema.account);
|
|
89
|
-
expect(schema.authSchema.verification).toBe(schema.verification);
|
|
90
|
-
expect(schema.authSchema.organization).toBe(schema.organization);
|
|
91
|
-
expect(schema.authSchema.member).toBe(schema.member);
|
|
92
|
-
expect(schema.authSchema.invitation).toBe(schema.invitation);
|
|
93
|
-
expect(schema.authSchema.jwks).toBe(schema.jwks);
|
|
94
|
-
expect(schema.authSchema.apikey).toBe(schema.apikey);
|
|
95
|
-
expect(schema.authSchema.userRelations).toBe(schema.userRelations);
|
|
96
|
-
expect(schema.authSchema.sessionRelations).toBe(schema.sessionRelations);
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('authSchema can be spread into another object', () => {
|
|
100
|
-
const appSchema = {
|
|
101
|
-
...schema.authSchema,
|
|
102
|
-
customTable: { name: 'custom' },
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
expect(appSchema.user).toBe(schema.user);
|
|
106
|
-
expect(appSchema.customTable).toEqual({ name: 'custom' });
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
describe('table columns', () => {
|
|
111
|
-
it('user table has required columns', () => {
|
|
112
|
-
const columns = Object.keys(schema.user);
|
|
113
|
-
expect(columns).toContain('id');
|
|
114
|
-
expect(columns).toContain('name');
|
|
115
|
-
expect(columns).toContain('email');
|
|
116
|
-
expect(columns).toContain('emailVerified');
|
|
117
|
-
expect(columns).toContain('createdAt');
|
|
118
|
-
expect(columns).toContain('updatedAt');
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('session table has required columns', () => {
|
|
122
|
-
const columns = Object.keys(schema.session);
|
|
123
|
-
expect(columns).toContain('id');
|
|
124
|
-
expect(columns).toContain('token');
|
|
125
|
-
expect(columns).toContain('userId');
|
|
126
|
-
expect(columns).toContain('expiresAt');
|
|
127
|
-
expect(columns).toContain('activeOrganizationId');
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it('apikey table has required columns', () => {
|
|
131
|
-
const columns = Object.keys(schema.apikey);
|
|
132
|
-
expect(columns).toContain('id');
|
|
133
|
-
expect(columns).toContain('key');
|
|
134
|
-
expect(columns).toContain('userId');
|
|
135
|
-
expect(columns).toContain('permissions');
|
|
136
|
-
expect(columns).toContain('metadata');
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it('organization table has required columns', () => {
|
|
140
|
-
const columns = Object.keys(schema.organization);
|
|
141
|
-
expect(columns).toContain('id');
|
|
142
|
-
expect(columns).toContain('name');
|
|
143
|
-
expect(columns).toContain('slug');
|
|
144
|
-
expect(columns).toContain('metadata');
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
});
|
package/tsconfig.json
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../tsconfig.base.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"composite": true,
|
|
5
|
-
"outDir": "./dist",
|
|
6
|
-
"rootDir": "./src",
|
|
7
|
-
"jsx": "react-jsx",
|
|
8
|
-
"lib": ["ESNext", "DOM"]
|
|
9
|
-
},
|
|
10
|
-
"include": ["src/**/*"],
|
|
11
|
-
"exclude": ["test/**/*"],
|
|
12
|
-
"references": [{ "path": "../react" }]
|
|
13
|
-
}
|
package/tsconfig.test.json
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../tsconfig.base.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"composite": false,
|
|
5
|
-
"outDir": "./dist-test",
|
|
6
|
-
"rootDir": ".",
|
|
7
|
-
"types": ["bun-types"]
|
|
8
|
-
},
|
|
9
|
-
"include": ["test/**/*", "src/**/*"],
|
|
10
|
-
"references": [{ "path": "../react" }, { "path": "../runtime" }, { "path": "../test-utils" }]
|
|
11
|
-
}
|