agent-credentials-e2e.test.mjs
88 lines 3.5 KB
Raw
sha256:e4c529f14a0bb908c1caaaeb3f95f3623a1a82e636e7e3722ca2cd3dc9821263 security: npm audit fix pre-bridge 2026-07-29 Human 40 days ago
1 /**
2 * Phase C — e2e: session mint + list + rotate + revoke against test router.
3 */
4
5 import { describe, it } from 'node:test';
6 import assert from 'node:assert/strict';
7 import fs from 'node:fs/promises';
8 import os from 'node:os';
9 import path from 'node:path';
10 import http from 'node:http';
11 import jwt from 'jsonwebtoken';
12 import express from 'express';
13 import { createAgentCredentialRouter } from '../hub/gateway/agent-credential-routes.mjs';
14 import { parseAgentCredential } from '../hub/lib/agent-credential-core.mjs';
15
16 const SECRET = 'phase-c-e2e-test-secret-value-32bytes!';
17
18 describe('Phase C e2e — agent credential lifecycle', () => {
19 it('mint list rotate revoke', async () => {
20 const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'kt-agent-e2e-'));
21 process.env.KNOWTATION_GATEWAY_DATA_DIR = dir;
22 const app = express();
23 const session = jwt.sign({ sub: 'github:42', type: 'session' }, SECRET, { expiresIn: '1h' });
24 const { router } = createAgentCredentialRouter({
25 sessionSecret: SECRET,
26 getSessionSub: () => 'github:42',
27 getSessionPayload: () => ({ sub: 'github:42', type: 'session' }),
28 grantedScopes: () => ['vault:read', 'vault:write'],
29 });
30 app.use('/api/v1/auth/agent', router);
31 const server = http.createServer(app);
32 await new Promise((r) => server.listen(0, r));
33 const base = `http://127.0.0.1:${server.address().port}`;
34 try {
35 const mint = await fetch(`${base}/api/v1/auth/agent/credentials`, {
36 method: 'POST',
37 headers: { Authorization: `Bearer ${session}`, 'Content-Type': 'application/json' },
38 body: JSON.stringify({ name: 'e2e', vault_ids: ['default'] }),
39 });
40 assert.equal(mint.status, 201);
41 const m = await mint.json();
42 const list = await (await fetch(`${base}/api/v1/auth/agent/credentials`, {
43 headers: { Authorization: `Bearer ${session}` },
44 })).json();
45 assert.equal(list.credentials.length, 1);
46 assert.equal(list.credentials[0].id, m.id);
47 assert.equal(list.credentials[0].credential, undefined);
48
49 const rot = await fetch(`${base}/api/v1/auth/agent/credentials/${m.id}/rotate`, {
50 method: 'POST',
51 headers: { Authorization: `Bearer ${session}` },
52 });
53 assert.equal(rot.status, 200);
54 const r = await rot.json();
55 assert.ok(parseAgentCredential(r.credential));
56 assert.notEqual(r.credential, m.credential);
57
58 const oldTok = await fetch(`${base}/api/v1/auth/agent/token`, {
59 method: 'POST',
60 headers: { 'Content-Type': 'application/json' },
61 body: JSON.stringify({ credential: m.credential }),
62 });
63 assert.equal(oldTok.status, 401);
64
65 const newTok = await fetch(`${base}/api/v1/auth/agent/token`, {
66 method: 'POST',
67 headers: { 'Content-Type': 'application/json' },
68 body: JSON.stringify({ credential: r.credential }),
69 });
70 assert.equal(newTok.status, 200);
71
72 await fetch(`${base}/api/v1/auth/agent/credentials/${m.id}`, {
73 method: 'DELETE',
74 headers: { Authorization: `Bearer ${session}` },
75 });
76 const after = await (await fetch(`${base}/api/v1/auth/agent/token`, {
77 method: 'POST',
78 headers: { 'Content-Type': 'application/json' },
79 body: JSON.stringify({ credential: r.credential }),
80 })).json();
81 assert.equal(after.code, 'AGENT_CREDENTIAL_INVALID');
82 } finally {
83 await new Promise((r) => server.close(r));
84 delete process.env.KNOWTATION_GATEWAY_DATA_DIR;
85 await fs.rm(dir, { recursive: true, force: true });
86 }
87 });
88 });
File History 1 commit
sha256:e4c529f14a0bb908c1caaaeb3f95f3623a1a82e636e7e3722ca2cd3dc9821263 security: npm audit fix pre-bridge 2026-07-29 Human 40 days ago