@slashfi/agents-sdk 0.24.1 → 0.24.3

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.
Files changed (69) hide show
  1. package/dist/callback/index.d.ts +13 -24
  2. package/dist/callback/index.d.ts.map +1 -1
  3. package/dist/callback/index.js.map +1 -1
  4. package/dist/cjs/agent-definitions/auth.js +678 -0
  5. package/dist/cjs/agent-definitions/auth.js.map +1 -0
  6. package/dist/cjs/agent-definitions/integrations.js +1173 -0
  7. package/dist/cjs/agent-definitions/integrations.js.map +1 -0
  8. package/dist/cjs/agent-definitions/remote-registry.js +469 -0
  9. package/dist/cjs/agent-definitions/remote-registry.js.map +1 -0
  10. package/dist/cjs/agent-definitions/secrets.js +193 -0
  11. package/dist/cjs/agent-definitions/secrets.js.map +1 -0
  12. package/dist/cjs/agent-definitions/users.js +440 -0
  13. package/dist/cjs/agent-definitions/users.js.map +1 -0
  14. package/dist/cjs/build.js +162 -0
  15. package/dist/cjs/build.js.map +1 -0
  16. package/dist/cjs/callback/index.js +74 -0
  17. package/dist/cjs/callback/index.js.map +1 -0
  18. package/dist/cjs/client.js +193 -0
  19. package/dist/cjs/client.js.map +1 -0
  20. package/dist/cjs/codegen.js +1027 -0
  21. package/dist/cjs/codegen.js.map +1 -0
  22. package/dist/cjs/crypto.js +44 -0
  23. package/dist/cjs/crypto.js.map +1 -0
  24. package/dist/cjs/define-config.js +81 -0
  25. package/dist/cjs/define-config.js.map +1 -0
  26. package/dist/cjs/define.js +186 -0
  27. package/dist/cjs/define.js.map +1 -0
  28. package/dist/cjs/events.js +60 -0
  29. package/dist/cjs/events.js.map +1 -0
  30. package/dist/cjs/index.js +195 -0
  31. package/dist/cjs/index.js.map +1 -0
  32. package/dist/cjs/integration-interface.js +105 -0
  33. package/dist/cjs/integration-interface.js.map +1 -0
  34. package/dist/cjs/integrations-store.js +53 -0
  35. package/dist/cjs/integrations-store.js.map +1 -0
  36. package/dist/cjs/introspect.js +136 -0
  37. package/dist/cjs/introspect.js.map +1 -0
  38. package/dist/cjs/jsonc.js +74 -0
  39. package/dist/cjs/jsonc.js.map +1 -0
  40. package/dist/cjs/jwt.js +207 -0
  41. package/dist/cjs/jwt.js.map +1 -0
  42. package/dist/cjs/key-manager.js +161 -0
  43. package/dist/cjs/key-manager.js.map +1 -0
  44. package/dist/cjs/oidc-signin.js +141 -0
  45. package/dist/cjs/oidc-signin.js.map +1 -0
  46. package/dist/cjs/pack.js +256 -0
  47. package/dist/cjs/pack.js.map +1 -0
  48. package/dist/cjs/package.json +1 -0
  49. package/dist/cjs/registry-consumer.js +233 -0
  50. package/dist/cjs/registry-consumer.js.map +1 -0
  51. package/dist/cjs/registry.js +512 -0
  52. package/dist/cjs/registry.js.map +1 -0
  53. package/dist/cjs/secret-collection.js +42 -0
  54. package/dist/cjs/secret-collection.js.map +1 -0
  55. package/dist/cjs/serialized.js +45 -0
  56. package/dist/cjs/serialized.js.map +1 -0
  57. package/dist/cjs/server.js +974 -0
  58. package/dist/cjs/server.js.map +1 -0
  59. package/dist/cjs/test-utils/mock-oidc-server.js +99 -0
  60. package/dist/cjs/test-utils/mock-oidc-server.js.map +1 -0
  61. package/dist/cjs/types.js +8 -0
  62. package/dist/cjs/types.js.map +1 -0
  63. package/dist/cjs/validate.js +84 -0
  64. package/dist/cjs/validate.js.map +1 -0
  65. package/dist/registry.js +6 -6
  66. package/dist/registry.js.map +1 -1
  67. package/package.json +13 -5
  68. package/src/callback/index.ts +13 -24
  69. package/src/registry.ts +6 -6
@@ -0,0 +1,678 @@
1
+ "use strict";
2
+ /**
3
+ * Auth Agent
4
+ *
5
+ * Built-in agent that provides OAuth2 client_credentials authentication.
6
+ * Register it into any agent registry to enable auth.
7
+ *
8
+ * Features:
9
+ * - Client credentials management (create, rotate, revoke)
10
+ * - OAuth2 client_credentials token exchange
11
+ * - JWT access tokens with scopes
12
+ * - Pluggable AuthStore interface (in-memory default)
13
+ * - Root key for admin operations
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * import { createAgentRegistry, createAgentServer, createAuthAgent } from '@slashfi/agents-sdk';
18
+ *
19
+ * const registry = createAgentRegistry();
20
+ * registry.register(createAuthAgent({ rootKey: process.env.ROOT_KEY }));
21
+ * registry.register(myAgent);
22
+ *
23
+ * const server = createAgentServer(registry, { port: 3000 });
24
+ * await server.start();
25
+ * ```
26
+ */
27
+ Object.defineProperty(exports, "__esModule", { value: true });
28
+ exports.createMemoryAuthStore = createMemoryAuthStore;
29
+ exports.createAuthAgent = createAuthAgent;
30
+ const define_js_1 = require("../define.js");
31
+ const jwt_js_1 = require("../jwt.js");
32
+ // ============================================
33
+ // In-Memory Auth Store
34
+ // ============================================
35
+ function generateId(prefix) {
36
+ const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
37
+ let id = prefix;
38
+ for (let i = 0; i < 24; i++) {
39
+ id += chars[Math.floor(Math.random() * chars.length)];
40
+ }
41
+ return id;
42
+ }
43
+ function generateSecret() {
44
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
45
+ let secret = "sk_";
46
+ for (let i = 0; i < 40; i++) {
47
+ secret += chars[Math.floor(Math.random() * chars.length)];
48
+ }
49
+ return secret;
50
+ }
51
+ /** Simple hash for storing secrets (not for production - use bcrypt/argon2) */
52
+ async function hashSecret(secret) {
53
+ const encoder = new TextEncoder();
54
+ const data = encoder.encode(secret);
55
+ const hash = await crypto.subtle.digest("SHA-256", data);
56
+ return Array.from(new Uint8Array(hash))
57
+ .map((b) => b.toString(16).padStart(2, "0"))
58
+ .join("");
59
+ }
60
+ /**
61
+ * Create an in-memory auth store.
62
+ * Suitable for development and testing. Use a persistent store for production.
63
+ */
64
+ function createMemoryAuthStore() {
65
+ const tenants = new Map();
66
+ const clients = new Map();
67
+ const tokens = new Map();
68
+ const signingKeys = new Map();
69
+ const trustedIssuers = new Set();
70
+ const tenantIdentities = new Map(); // "provider:providerTenantId" -> tenantId
71
+ const userIdentities = new Map(); // "provider:providerUserId" -> userId
72
+ const refreshTokens = new Map();
73
+ return {
74
+ async createTenant(name, _externalRef) {
75
+ const id = `tenant_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
76
+ tenants.set(id, { id, name, createdAt: Date.now() });
77
+ return { tenantId: id };
78
+ },
79
+ async getTenant(tenantId) {
80
+ return tenants.get(tenantId) ?? null;
81
+ },
82
+ async listTenants() {
83
+ return Array.from(tenants.values());
84
+ },
85
+ async createClient(name, scopes, selfRegistered, tenantId) {
86
+ const clientId = generateId("ag_");
87
+ const clientSecret = generateSecret();
88
+ const secretHash = await hashSecret(clientSecret);
89
+ clients.set(clientId, {
90
+ clientId,
91
+ tenantId,
92
+ clientSecretHash: secretHash,
93
+ name,
94
+ scopes,
95
+ createdAt: Date.now(),
96
+ selfRegistered,
97
+ });
98
+ return { clientId, clientSecret };
99
+ },
100
+ async validateClient(clientId, clientSecret) {
101
+ const client = clients.get(clientId);
102
+ if (!client)
103
+ return null;
104
+ const hash = await hashSecret(clientSecret);
105
+ return hash === client.clientSecretHash ? client : null;
106
+ },
107
+ async getClient(clientId) {
108
+ return clients.get(clientId) ?? null;
109
+ },
110
+ async listClients() {
111
+ return Array.from(clients.values());
112
+ },
113
+ async revokeClient(clientId) {
114
+ // Also revoke all tokens for this client
115
+ for (const [tokenStr, token] of tokens) {
116
+ if (token.clientId === clientId) {
117
+ tokens.delete(tokenStr);
118
+ }
119
+ }
120
+ return clients.delete(clientId);
121
+ },
122
+ async rotateSecret(clientId) {
123
+ const client = clients.get(clientId);
124
+ if (!client)
125
+ return null;
126
+ const clientSecret = generateSecret();
127
+ client.clientSecretHash = await hashSecret(clientSecret);
128
+ return {
129
+ clientSecret: { $agent_type: "secret", value: clientSecret },
130
+ };
131
+ },
132
+ async storeToken(token) {
133
+ tokens.set(token.token, token);
134
+ },
135
+ async validateToken(tokenString) {
136
+ const token = tokens.get(tokenString);
137
+ if (!token)
138
+ return null;
139
+ if (Date.now() > token.expiresAt) {
140
+ tokens.delete(tokenString);
141
+ return null;
142
+ }
143
+ return token;
144
+ },
145
+ async revokeToken(tokenString) {
146
+ return tokens.delete(tokenString);
147
+ },
148
+ // --- Signing Keys ---
149
+ async storeSigningKey(key) {
150
+ signingKeys.set(key.kid, key);
151
+ },
152
+ async getSigningKeys() {
153
+ return Array.from(signingKeys.values()).filter((k) => k.status !== "revoked");
154
+ },
155
+ async getActiveSigningKey() {
156
+ for (const key of signingKeys.values()) {
157
+ if (key.status === "active")
158
+ return key;
159
+ }
160
+ return null;
161
+ },
162
+ async deprecateSigningKey(kid) {
163
+ const key = signingKeys.get(kid);
164
+ if (!key)
165
+ return false;
166
+ key.status = "deprecated";
167
+ return true;
168
+ },
169
+ async revokeSigningKey(kid) {
170
+ return signingKeys.delete(kid);
171
+ },
172
+ // --- Trusted Issuers ---
173
+ async addTrustedIssuer(issuerUrl) {
174
+ trustedIssuers.add(issuerUrl);
175
+ },
176
+ async removeTrustedIssuer(issuerUrl) {
177
+ return trustedIssuers.delete(issuerUrl);
178
+ },
179
+ async listTrustedIssuers() {
180
+ return Array.from(trustedIssuers);
181
+ },
182
+ async storeTenantIdentity(tenantId, provider, providerTenantId) {
183
+ tenantIdentities.set(`${provider}:${providerTenantId}`, tenantId);
184
+ },
185
+ async resolveTenantByIdentity(provider, providerTenantId) {
186
+ return tenantIdentities.get(`${provider}:${providerTenantId}`) ?? null;
187
+ },
188
+ async storeUserIdentity(userId, provider, providerUserId) {
189
+ userIdentities.set(`${provider}:${providerUserId}`, userId);
190
+ },
191
+ async resolveUserByIdentity(provider, providerUserId) {
192
+ return userIdentities.get(`${provider}:${providerUserId}`) ?? null;
193
+ },
194
+ async registerUser(tenantId, userId, clientId) {
195
+ const refreshToken = `rt_${generateId("rt")}`;
196
+ refreshTokens.set(refreshToken, { tenantId, userId, clientId });
197
+ return { refreshToken };
198
+ },
199
+ async validateRefreshToken(refreshToken) {
200
+ return refreshTokens.get(refreshToken) ?? null;
201
+ },
202
+ async rotateRefreshToken(oldToken) {
203
+ const data = refreshTokens.get(oldToken);
204
+ if (!data)
205
+ return null;
206
+ refreshTokens.delete(oldToken);
207
+ const refreshToken = `rt_${generateId("rt")}`;
208
+ refreshTokens.set(refreshToken, data);
209
+ return { refreshToken, ...data };
210
+ },
211
+ async transaction(fn) {
212
+ // In-memory: just run sequentially (single-threaded, no concurrency issues)
213
+ return fn();
214
+ },
215
+ };
216
+ }
217
+ // ============================================
218
+ // Create Auth Agent
219
+ // ============================================
220
+ /**
221
+ * Create the built-in `@auth` agent.
222
+ *
223
+ * Provides OAuth2 client_credentials authentication as agent tools.
224
+ * The server auto-detects this agent and wires up token validation.
225
+ */
226
+ function createAuthAgent(options) {
227
+ const { rootKey, allowRegistration = false, registrationScopes, tokenTtl = 3600, store = createMemoryAuthStore(), } = options;
228
+ // --- Public Tools ---
229
+ const createTenantTool = (0, define_js_1.defineTool)({
230
+ name: "create_tenant",
231
+ description: "Create a new tenant (organizational unit). All clients and resources are scoped to a tenant.",
232
+ visibility: "public",
233
+ inputSchema: {
234
+ type: "object",
235
+ properties: {
236
+ name: { type: "string", description: "Tenant name" },
237
+ externalRef: {
238
+ type: "object",
239
+ description: "Link to a tenant on a remote system (for cross-registry trust)",
240
+ properties: {
241
+ issuer: {
242
+ type: "string",
243
+ description: "Issuer URL of the remote system",
244
+ },
245
+ tenantId: {
246
+ type: "string",
247
+ description: "Tenant ID on the remote system",
248
+ },
249
+ },
250
+ required: ["issuer", "tenantId"],
251
+ },
252
+ },
253
+ required: ["name"],
254
+ },
255
+ execute: async (input) => {
256
+ const result = await store.createTenant(input.name, input.externalRef);
257
+ return {
258
+ tenantId: result.tenantId,
259
+ name: input.name,
260
+ externalRef: input.externalRef,
261
+ };
262
+ },
263
+ });
264
+ const tokenTool = (0, define_js_1.defineTool)({
265
+ name: "token",
266
+ description: "Exchange client credentials for an access token (OAuth2 client_credentials grant)",
267
+ visibility: "public",
268
+ inputSchema: {
269
+ type: "object",
270
+ properties: {
271
+ grantType: {
272
+ type: "string",
273
+ enum: ["client_credentials"],
274
+ description: "Grant type. Must be 'client_credentials'.",
275
+ },
276
+ clientId: { type: "string", description: "Client ID" },
277
+ clientSecret: { type: "string", description: "Client secret" },
278
+ },
279
+ required: ["grantType", "clientId", "clientSecret"],
280
+ },
281
+ execute: async (input) => {
282
+ if (input.grantType === "refresh_token") {
283
+ if (!input.refreshToken)
284
+ throw new Error("refreshToken is required for refresh_token grant");
285
+ const result = await store.rotateRefreshToken(input.refreshToken);
286
+ if (!result)
287
+ throw new Error("Invalid or expired refresh token");
288
+ const now = Math.floor(Date.now() / 1000);
289
+ const jwt = await (0, jwt_js_1.signJwt)({
290
+ sub: result.clientId,
291
+ name: result.userId,
292
+ tenantId: result.tenantId,
293
+ scopes: [],
294
+ iat: now,
295
+ exp: now + tokenTtl,
296
+ }, (await store.getClient(result.clientId))?.clientSecretHash ?? "");
297
+ return {
298
+ accessToken: { $agent_type: "secret", value: jwt },
299
+ refreshToken: { $agent_type: "secret", value: result.refreshToken },
300
+ tokenType: "bearer",
301
+ expiresIn: tokenTtl,
302
+ };
303
+ }
304
+ if (input.grantType !== "client_credentials") {
305
+ throw new Error("Unsupported grant type. Use 'client_credentials' or 'refresh_token'.");
306
+ }
307
+ const client = await store.validateClient(input.clientId, input.clientSecret);
308
+ if (!client) {
309
+ throw new Error("Invalid client credentials");
310
+ }
311
+ const now = Math.floor(Date.now() / 1000);
312
+ const jwt = await (0, jwt_js_1.signJwt)({
313
+ sub: client.clientId,
314
+ name: client.name,
315
+ tenantId: client.tenantId,
316
+ scopes: client.scopes,
317
+ iat: now,
318
+ exp: now + tokenTtl,
319
+ }, client.clientSecretHash);
320
+ return {
321
+ accessToken: { $agent_type: "secret", value: jwt },
322
+ tokenType: "bearer",
323
+ expiresIn: tokenTtl,
324
+ scopes: client.scopes,
325
+ };
326
+ },
327
+ });
328
+ const whoamiTool = (0, define_js_1.defineTool)({
329
+ name: "whoami",
330
+ description: "Introspect the current authentication context",
331
+ visibility: "public",
332
+ inputSchema: { type: "object", properties: {} },
333
+ execute: async (_input, ctx) => {
334
+ return {
335
+ callerId: ctx.callerId,
336
+ callerType: ctx.callerType,
337
+ scopes: ctx.scopes ?? [],
338
+ isRoot: ctx.callerId === "root",
339
+ };
340
+ },
341
+ });
342
+ // --- Optional Public Tool ---
343
+ const registerTool = (0, define_js_1.defineTool)({
344
+ name: "register",
345
+ description: "Register a new agent client (self-service)",
346
+ visibility: "public",
347
+ inputSchema: {
348
+ type: "object",
349
+ properties: {
350
+ name: { type: "string", description: "Name for this client" },
351
+ scopes: {
352
+ type: "array",
353
+ items: { type: "string" },
354
+ description: "Requested scopes",
355
+ },
356
+ },
357
+ required: ["name"],
358
+ },
359
+ execute: async (input) => {
360
+ let scopes = input.scopes ?? [];
361
+ // If registration scopes are restricted, filter
362
+ if (registrationScopes && registrationScopes.length > 0) {
363
+ scopes = scopes.filter((s) => registrationScopes.includes(s));
364
+ }
365
+ const { clientId, clientSecret } = await store.createClient(input.name, scopes, true, input.tenantId);
366
+ return {
367
+ clientId,
368
+ clientSecret: { $agent_type: "secret", value: clientSecret },
369
+ scopes,
370
+ };
371
+ },
372
+ });
373
+ // --- Private Tools (root key only) ---
374
+ const createClientTool = (0, define_js_1.defineTool)({
375
+ name: "create_client",
376
+ description: "Create a new client with specific scopes (admin only)",
377
+ visibility: "private",
378
+ inputSchema: {
379
+ type: "object",
380
+ properties: {
381
+ name: { type: "string", description: "Client name" },
382
+ scopes: {
383
+ type: "array",
384
+ items: { type: "string" },
385
+ description: "Scopes to grant",
386
+ },
387
+ },
388
+ required: ["name", "scopes"],
389
+ },
390
+ execute: async (input) => {
391
+ const { clientId, clientSecret } = await store.createClient(input.name, input.scopes);
392
+ return { clientId, clientSecret, scopes: input.scopes };
393
+ },
394
+ });
395
+ const listClientsTool = (0, define_js_1.defineTool)({
396
+ name: "list_clients",
397
+ description: "List all registered clients (admin only)",
398
+ visibility: "private",
399
+ inputSchema: { type: "object", properties: {} },
400
+ execute: async () => {
401
+ const clients = await store.listClients();
402
+ return {
403
+ clients: clients.map((c) => ({
404
+ clientId: c.clientId,
405
+ name: c.name,
406
+ scopes: c.scopes,
407
+ createdAt: c.createdAt,
408
+ selfRegistered: c.selfRegistered ?? false,
409
+ })),
410
+ };
411
+ },
412
+ });
413
+ const revokeClientTool = (0, define_js_1.defineTool)({
414
+ name: "revoke_client",
415
+ description: "Revoke a client and all its tokens (admin only)",
416
+ visibility: "private",
417
+ inputSchema: {
418
+ type: "object",
419
+ properties: {
420
+ clientId: { type: "string", description: "Client ID to revoke" },
421
+ },
422
+ required: ["clientId"],
423
+ },
424
+ execute: async (input) => {
425
+ const revoked = await store.revokeClient(input.clientId);
426
+ return { revoked };
427
+ },
428
+ });
429
+ const rotateSecretTool = (0, define_js_1.defineTool)({
430
+ name: "rotate_secret",
431
+ description: "Rotate a client's secret (admin only)",
432
+ visibility: "private",
433
+ inputSchema: {
434
+ type: "object",
435
+ properties: {
436
+ clientId: { type: "string", description: "Client ID to rotate" },
437
+ },
438
+ required: ["clientId"],
439
+ },
440
+ execute: async (input) => {
441
+ const result = await store.rotateSecret(input.clientId);
442
+ if (!result)
443
+ throw new Error(`Client not found: ${input.clientId}`);
444
+ return { clientId: input.clientId, clientSecret: result.clientSecret };
445
+ },
446
+ });
447
+ // --- Assemble tools ---
448
+ // --- Key Management Tools ---
449
+ const rotateKeysTool = (0, define_js_1.defineTool)({
450
+ name: "rotate_keys",
451
+ description: "Generate a new ES256 signing key and deprecate the current active key. The old key remains valid for verification during the overlap period.",
452
+ visibility: "private",
453
+ inputSchema: {
454
+ type: "object",
455
+ properties: {},
456
+ },
457
+ execute: async () => {
458
+ // Deprecate current active key
459
+ const current = await store.getActiveSigningKey();
460
+ if (current) {
461
+ await store.deprecateSigningKey(current.kid);
462
+ }
463
+ // Generate and store new key
464
+ const newKey = await (0, jwt_js_1.generateSigningKey)();
465
+ const exported = await (0, jwt_js_1.exportSigningKey)(newKey);
466
+ await store.storeSigningKey(exported);
467
+ return {
468
+ newKid: newKey.kid,
469
+ deprecatedKid: current?.kid ?? null,
470
+ message: "New signing key generated. Old key deprecated but still valid for verification.",
471
+ };
472
+ },
473
+ });
474
+ const apiKeyTool = (0, define_js_1.defineTool)({
475
+ name: "api_key",
476
+ description: "Create or list API keys for MCP access.",
477
+ visibility: "authenticated",
478
+ inputSchema: {
479
+ type: "object",
480
+ properties: {
481
+ action: {
482
+ type: "string",
483
+ enum: ["create", "list"],
484
+ description: "Action",
485
+ },
486
+ name: { type: "string", description: "Key name" },
487
+ scopes: {
488
+ type: "array",
489
+ items: { type: "string" },
490
+ description: "Scopes",
491
+ },
492
+ },
493
+ required: ["action"],
494
+ },
495
+ execute: async (input) => {
496
+ if (input.action === "create") {
497
+ const result = await store.createClient(input.name ?? "api-key", input.scopes ?? ["*"], false);
498
+ return { key: result.clientSecret, clientId: result.clientId };
499
+ }
500
+ if (input.action === "list") {
501
+ const clients = await store.listClients();
502
+ return {
503
+ keys: clients.map((c) => ({
504
+ id: c.clientId,
505
+ name: c.name,
506
+ scopes: c.scopes,
507
+ })),
508
+ };
509
+ }
510
+ return { error: "Unknown action" };
511
+ },
512
+ });
513
+ const trustIssuerTool = (0, define_js_1.defineTool)({
514
+ name: "trust_issuer",
515
+ description: "Add or remove a trusted issuer URL. JWTs from trusted issuers are verified against their JWKS endpoint.",
516
+ visibility: "private",
517
+ inputSchema: {
518
+ type: "object",
519
+ properties: {
520
+ action: {
521
+ type: "string",
522
+ enum: ["add", "remove", "list"],
523
+ description: "Action to perform",
524
+ },
525
+ issuerUrl: {
526
+ type: "string",
527
+ description: "Issuer URL (required for add/remove)",
528
+ },
529
+ },
530
+ required: ["action"],
531
+ },
532
+ execute: async (input) => {
533
+ switch (input.action) {
534
+ case "add": {
535
+ if (!input.issuerUrl)
536
+ throw new Error("issuerUrl is required");
537
+ await store.addTrustedIssuer(input.issuerUrl);
538
+ return {
539
+ success: true,
540
+ message: `Added trusted issuer: ${input.issuerUrl}`,
541
+ };
542
+ }
543
+ case "remove": {
544
+ if (!input.issuerUrl)
545
+ throw new Error("issuerUrl is required");
546
+ const removed = await store.removeTrustedIssuer(input.issuerUrl);
547
+ return {
548
+ success: removed,
549
+ message: removed ? "Removed" : "Not found",
550
+ };
551
+ }
552
+ case "list": {
553
+ const issuers = await store.listTrustedIssuers();
554
+ return { issuers };
555
+ }
556
+ }
557
+ },
558
+ });
559
+ const exchangeTokenTool = (0, define_js_1.defineTool)({
560
+ name: "exchange_token",
561
+ description: "Exchange a foreign JWT for a local identity. Verifies the JWT via JWKS, " +
562
+ "resolves the tenant and user to local IDs. If the user is not yet linked, " +
563
+ "returns needsAuth=true with a connect URL for OAuth identity linking.",
564
+ visibility: "public",
565
+ inputSchema: {
566
+ type: "object",
567
+ properties: {
568
+ token: {
569
+ type: "string",
570
+ description: "JWT signed by a trusted issuer",
571
+ },
572
+ },
573
+ required: ["token"],
574
+ },
575
+ execute: async (input) => {
576
+ // 1. Decode JWT to read iss claim (no verification yet)
577
+ const parts = input.token.split(".");
578
+ if (parts.length !== 3) {
579
+ return { success: false, error: "Invalid JWT format" };
580
+ }
581
+ let decoded;
582
+ try {
583
+ decoded = JSON.parse(Buffer.from(parts[1], "base64url").toString());
584
+ }
585
+ catch {
586
+ return { success: false, error: "Failed to decode JWT payload" };
587
+ }
588
+ const issuer = decoded.iss;
589
+ const sub = decoded.sub;
590
+ const foreignTenantId = decoded.tenantId;
591
+ if (!issuer || !sub) {
592
+ return { success: false, error: "JWT missing iss or sub claims" };
593
+ }
594
+ // 2. Match issuer against trusted issuers
595
+ const trustedIssuers = await store.listTrustedIssuers();
596
+ if (!trustedIssuers.includes(issuer)) {
597
+ return { success: false, error: `Issuer ${issuer} is not trusted` };
598
+ }
599
+ // 3. Verify JWT against the matched issuer's JWKS
600
+ let payload;
601
+ try {
602
+ payload = await (0, jwt_js_1.verifyJwtFromIssuer)(input.token, issuer);
603
+ }
604
+ catch {
605
+ return { success: false, error: "JWT verification failed" };
606
+ }
607
+ if (!payload) {
608
+ return {
609
+ success: false,
610
+ error: "JWT verification returned empty payload",
611
+ };
612
+ }
613
+ // 4. Resolve tenant + user inside a transaction for consistency
614
+ return store.transaction(async () => {
615
+ const localTenantId = await (async () => {
616
+ if (!foreignTenantId)
617
+ return null;
618
+ const existing = await store.resolveTenantByIdentity(issuer, foreignTenantId);
619
+ if (existing)
620
+ return existing;
621
+ // Auto-create tenant identity link on first encounter
622
+ await store.storeTenantIdentity(foreignTenantId, issuer, foreignTenantId);
623
+ return foreignTenantId;
624
+ })();
625
+ // 5. Resolve user
626
+ const localUserId = await store.resolveUserByIdentity(issuer, sub);
627
+ if (localUserId) {
628
+ return {
629
+ success: true,
630
+ tenantId: localTenantId ?? undefined,
631
+ userId: localUserId,
632
+ };
633
+ }
634
+ // 4. User not linked — caller decides how to handle (e.g. OIDC flow)
635
+ return {
636
+ success: false,
637
+ needsAuth: true,
638
+ tenantId: localTenantId ?? undefined,
639
+ issuer,
640
+ sub,
641
+ };
642
+ });
643
+ },
644
+ });
645
+ const tools = [
646
+ createTenantTool,
647
+ tokenTool,
648
+ whoamiTool,
649
+ ...(allowRegistration ? [registerTool] : []),
650
+ createClientTool,
651
+ listClientsTool,
652
+ revokeClientTool,
653
+ rotateSecretTool,
654
+ rotateKeysTool,
655
+ trustIssuerTool,
656
+ apiKeyTool,
657
+ exchangeTokenTool,
658
+ ];
659
+ const agent = (0, define_js_1.defineAgent)({
660
+ path: "@auth",
661
+ entrypoint: "Authentication agent. Provides OAuth2 client_credentials authentication for the agent network.",
662
+ config: {
663
+ name: "Auth",
664
+ visibility: "public",
665
+ description: "Built-in authentication agent",
666
+ supportedActions: ["execute_tool", "describe_tools", "load"],
667
+ },
668
+ tools: tools,
669
+ visibility: "public",
670
+ });
671
+ // Attach store and config for server integration
672
+ return Object.assign(agent, {
673
+ __authStore: store,
674
+ __rootKey: rootKey,
675
+ __tokenTtl: tokenTtl,
676
+ });
677
+ }
678
+ //# sourceMappingURL=auth.js.map