@slashfi/agents-sdk 0.11.2 → 0.13.0

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 (48) hide show
  1. package/dist/agent-definitions/auth.d.ts +17 -1
  2. package/dist/agent-definitions/auth.d.ts.map +1 -1
  3. package/dist/agent-definitions/auth.js +123 -4
  4. package/dist/agent-definitions/auth.js.map +1 -1
  5. package/dist/agent-definitions/integrations.d.ts +2 -14
  6. package/dist/agent-definitions/integrations.d.ts.map +1 -1
  7. package/dist/agent-definitions/integrations.js +64 -19
  8. package/dist/agent-definitions/integrations.js.map +1 -1
  9. package/dist/agent-definitions/remote-registry.d.ts +19 -14
  10. package/dist/agent-definitions/remote-registry.d.ts.map +1 -1
  11. package/dist/agent-definitions/remote-registry.js +219 -381
  12. package/dist/agent-definitions/remote-registry.js.map +1 -1
  13. package/dist/agent-definitions/users.d.ts.map +1 -1
  14. package/dist/agent-definitions/users.js +29 -1
  15. package/dist/agent-definitions/users.js.map +1 -1
  16. package/dist/define.d.ts +6 -4
  17. package/dist/define.d.ts.map +1 -1
  18. package/dist/define.js +82 -3
  19. package/dist/define.js.map +1 -1
  20. package/dist/index.d.ts +3 -2
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +1 -0
  23. package/dist/index.js.map +1 -1
  24. package/dist/jwt.js +1 -1
  25. package/dist/jwt.js.map +1 -1
  26. package/dist/key-manager.d.ts +76 -0
  27. package/dist/key-manager.d.ts.map +1 -0
  28. package/dist/key-manager.js +156 -0
  29. package/dist/key-manager.js.map +1 -0
  30. package/dist/server.d.ts +39 -0
  31. package/dist/server.d.ts.map +1 -1
  32. package/dist/server.js +228 -52
  33. package/dist/server.js.map +1 -1
  34. package/dist/types.d.ts +53 -1
  35. package/dist/types.d.ts.map +1 -1
  36. package/package.json +1 -1
  37. package/src/agent-definitions/auth.ts +165 -6
  38. package/src/agent-definitions/integrations.ts +59 -28
  39. package/src/agent-definitions/remote-registry.ts +219 -513
  40. package/src/agent-definitions/users.ts +35 -1
  41. package/src/define.ts +98 -6
  42. package/src/index.ts +3 -1
  43. package/src/jwt.ts +1 -1
  44. package/src/key-manager.test.ts +273 -0
  45. package/src/key-manager.ts +257 -0
  46. package/src/server.test.ts +284 -0
  47. package/src/server.ts +334 -60
  48. package/src/types.ts +44 -1
@@ -0,0 +1,156 @@
1
+ /**
2
+ * JWKS Key Manager
3
+ *
4
+ * Manages ES256 signing keys with automatic rotation and revocation.
5
+ * Store-agnostic — provide a KeyStore implementation for your DB.
6
+ *
7
+ * Features:
8
+ * - Automatic key rotation on a configurable schedule
9
+ * - Key lifecycle: active → deprecated → revoked → cleaned up
10
+ * - Multi-instance safe: checks DB before rotating (another instance may have already rotated)
11
+ * - Periodic background checks (configurable interval)
12
+ * - Exposes JWKS for /.well-known/jwks.json
13
+ * - Signs JWTs with the active key
14
+ */
15
+ import { generateKeyPair, exportJWK, importJWK, SignJWT, } from "jose";
16
+ // ── Constants ──
17
+ const FIVE_MINUTES = 5 * 60 * 1000;
18
+ const ONE_HOUR = 60 * 60 * 1000;
19
+ const TWO_HOURS = 2 * ONE_HOUR;
20
+ const ALG = "ES256";
21
+ // ── Key generation ──
22
+ function generateKid() {
23
+ return `key-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
24
+ }
25
+ async function generateNewKey(keyLifetimeMs) {
26
+ const { privateKey, publicKey } = await generateKeyPair(ALG, { extractable: true });
27
+ const kid = generateKid();
28
+ const publicJwk = await exportJWK(publicKey);
29
+ publicJwk.kid = kid;
30
+ publicJwk.alg = ALG;
31
+ publicJwk.use = "sig";
32
+ const privateJwk = await exportJWK(privateKey);
33
+ privateJwk.kid = kid;
34
+ privateJwk.alg = ALG;
35
+ return {
36
+ kid,
37
+ alg: ALG,
38
+ status: "active",
39
+ publicJwk,
40
+ privateJwk,
41
+ createdAt: new Date(),
42
+ expiresAt: new Date(Date.now() + keyLifetimeMs),
43
+ };
44
+ }
45
+ async function toCachedKey(stored) {
46
+ const privateKey = await importJWK(stored.privateJwk, ALG);
47
+ return { ...stored, privateKey };
48
+ }
49
+ // ── Key Manager ──
50
+ export async function createKeyManager(opts) {
51
+ const { store, issuer, checkIntervalMs = FIVE_MINUTES, rotationThresholdMs = ONE_HOUR, keyLifetimeMs = TWO_HOURS, tokenTtlSeconds = 300, enableRotation = true, } = opts;
52
+ let keys = [];
53
+ /** Generate a new key, deprecate old ones, cleanup expired, refresh cache — all in one transaction */
54
+ async function rotate() {
55
+ await store.transaction(async () => {
56
+ const newKey = await generateNewKey(keyLifetimeMs);
57
+ await store.deprecateAllActive();
58
+ await store.insertKey(newKey);
59
+ await store.cleanupExpired();
60
+ const updated = await store.loadKeys();
61
+ keys = await Promise.all(updated.map(toCachedKey));
62
+ });
63
+ }
64
+ /** Check if rotation is needed and rotate if so — all within a single transaction */
65
+ async function checkAndRotate() {
66
+ // Quick check against cache — no DB/store hit if key is fresh
67
+ const cached = keys.find((k) => k.status === "active");
68
+ if (cached) {
69
+ const age = Date.now() - cached.createdAt.getTime();
70
+ if (age < rotationThresholdMs)
71
+ return;
72
+ }
73
+ // Cache says stale (or empty) — take a lock via transaction to check + rotate atomically
74
+ await store.transaction(async () => {
75
+ const stored = await store.loadKeys();
76
+ const active = stored.find((k) => k.status === "active");
77
+ if (active) {
78
+ const age = Date.now() - active.createdAt.getTime();
79
+ if (age < rotationThresholdMs) {
80
+ // Another instance already rotated — just update our cache
81
+ keys = await Promise.all(stored.map(toCachedKey));
82
+ return;
83
+ }
84
+ }
85
+ // Still stale (or no active key) — rotate within this tx
86
+ const newKey = await generateNewKey(keyLifetimeMs);
87
+ await store.deprecateAllActive();
88
+ await store.insertKey(newKey);
89
+ await store.cleanupExpired();
90
+ // Refresh cache inside the tx for a consistent read
91
+ const updated = await store.loadKeys();
92
+ keys = await Promise.all(updated.map(toCachedKey));
93
+ });
94
+ }
95
+ // Initial load + ensure we have at least one key
96
+ // Initial load from store
97
+ const stored = await store.loadKeys();
98
+ keys = await Promise.all(stored.map(toCachedKey));
99
+ if (!keys.some((k) => k.status === "active")) {
100
+ if (enableRotation) {
101
+ await rotate();
102
+ }
103
+ else {
104
+ // Read-only mode: generate a key in memory only (no store writes)
105
+ // This ensures signJwt works even without rotation enabled
106
+ const newKey = await generateNewKey(keyLifetimeMs);
107
+ keys.push(await toCachedKey(newKey));
108
+ }
109
+ }
110
+ else if (enableRotation) {
111
+ await checkAndRotate();
112
+ }
113
+ // Periodic background check (only if rotation enabled)
114
+ const interval = enableRotation
115
+ ? setInterval(async () => {
116
+ try {
117
+ await checkAndRotate();
118
+ }
119
+ catch (err) {
120
+ console.error("[key-manager] Check/rotation failed:", err);
121
+ }
122
+ }, checkIntervalMs)
123
+ : null;
124
+ function getActiveKey() {
125
+ const active = keys.find((k) => k.status === "active");
126
+ if (!active)
127
+ throw new Error("[key-manager] No active signing key");
128
+ return active;
129
+ }
130
+ return {
131
+ getJwks() {
132
+ return {
133
+ keys: keys
134
+ .filter((k) => k.status !== "revoked")
135
+ .map((k) => k.publicJwk),
136
+ };
137
+ },
138
+ async signJwt(claims) {
139
+ const key = getActiveKey();
140
+ return new SignJWT({ ...claims })
141
+ .setProtectedHeader({ alg: ALG, kid: key.kid })
142
+ .setIssuer(issuer)
143
+ .setIssuedAt()
144
+ .setExpirationTime(`${tokenTtlSeconds}s`)
145
+ .sign(key.privateKey);
146
+ },
147
+ async rotate() {
148
+ await rotate();
149
+ },
150
+ stop() {
151
+ if (interval)
152
+ clearInterval(interval);
153
+ },
154
+ };
155
+ }
156
+ //# sourceMappingURL=key-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key-manager.js","sourceRoot":"","sources":["../src/key-manager.ts"],"names":[],"mappings":"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"}
package/dist/server.d.ts CHANGED
@@ -36,6 +36,34 @@ export interface TrustedIssuer {
36
36
  /** Scopes granted to tokens from this issuer */
37
37
  scopes: string[];
38
38
  }
39
+ /** OAuth identity provider for /oauth/authorize + /oauth/callback flows */
40
+ export interface OAuthIdentityProvider {
41
+ /**
42
+ * Handle /oauth/authorize — redirect the user to an external IdP.
43
+ * Return a Response (typically a 302 redirect).
44
+ */
45
+ authorize(req: Request, params: {
46
+ /** The verified JWT from the foreign registry */
47
+ token: string;
48
+ /** Claims from the verified JWT */
49
+ claims: Record<string, unknown>;
50
+ /** Where to redirect after completion */
51
+ redirectUri: string;
52
+ /** Base URL of this server */
53
+ baseUrl: string;
54
+ /** OAuth scope (e.g. "setup" for tenant creation flow) */
55
+ scope?: string;
56
+ }): Promise<Response>;
57
+ /**
58
+ * Handle /oauth/callback — process the IdP response.
59
+ * Should link the foreign identity to a local user and redirect back.
60
+ * Return a Response (typically a 302 redirect to redirectUri).
61
+ */
62
+ callback(req: Request, params: {
63
+ /** Base URL of this server */
64
+ baseUrl: string;
65
+ }): Promise<Response>;
66
+ }
39
67
  export interface AgentServerOptions {
40
68
  /** Port to listen on (default: 3000) */
41
69
  port?: number;
@@ -55,8 +83,14 @@ export interface AgentServerOptions {
55
83
  trustedIssuers?: (TrustedIssuer | string)[];
56
84
  /** Pre-generated signing key (if not provided, one is generated on start) */
57
85
  signingKey?: SigningKey;
86
+ /** OAuth identity provider for cross-registry user linking */
87
+ oauthIdentityProvider?: OAuthIdentityProvider;
88
+ /** Key store for managed key rotation (if provided, uses createKeyManager instead of simple key gen) */
89
+ keyStore?: import("./key-manager.js").KeyStore;
58
90
  }
59
91
  export interface AgentServer {
92
+ /** Initialize signing keys without starting HTTP server */
93
+ initKeys(): Promise<void>;
60
94
  /** Start the server */
61
95
  start(): Promise<void>;
62
96
  /** Stop the server */
@@ -67,6 +101,10 @@ export interface AgentServer {
67
101
  url: string | null;
68
102
  /** The agent registry this server uses */
69
103
  registry: AgentRegistry;
104
+ /** Sign a JWT with the server's signing key (for outbound calls) */
105
+ signJwt(claims: Record<string, unknown>): Promise<string>;
106
+ /** Dynamically add a trusted JWT issuer at runtime */
107
+ addTrustedIssuer(issuerUrl: string, scopes?: string[]): void;
70
108
  }
71
109
  export interface AuthConfig {
72
110
  store?: AuthStore;
@@ -74,6 +112,7 @@ export interface AuthConfig {
74
112
  tokenTtl?: number;
75
113
  }
76
114
  export interface ResolvedAuth {
115
+ issuer?: string;
77
116
  callerId: string;
78
117
  callerType: "agent" | "user" | "system";
79
118
  scopes: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EACL,KAAK,WAAW,EAEjB,MAAM,gCAAgC,CAAC;AAExC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,eAAe,EAAgC,MAAM,YAAY,CAAC;AAMhF,6DAA6D;AAC7D,MAAM,WAAW,aAAa;IAC5B,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC;IACf,gDAAgD;IAChD,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,wCAAwC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,iDAAiD;IACjD,cAAc,CAAC,EAAE,CAAC,aAAa,GAAG,MAAM,CAAC,EAAE,CAAC;IAC5C,6EAA6E;IAC7E,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,uBAAuB;IACvB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,sBAAsB;IACtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,yEAAyE;IACzE,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IACvC,sDAAsD;IACtD,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,0CAA0C;IAC1C,QAAQ,EAAE,aAAa,CAAC;CACzB;AAwBD,MAAM,WAAW,UAAU;IACzB,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;IACxC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;CACjB;AAqED,wBAAgB,UAAU,CAAC,QAAQ,EAAE,aAAa,GAAG,UAAU,CAgB9D;AAED,wBAAsB,WAAW,CAC/B,GAAG,EAAE,OAAO,EACZ,UAAU,EAAE,UAAU,EACtB,WAAW,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAAC,cAAc,CAAC,EAAE,aAAa,EAAE,CAAA;CAAE,GAC7E,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CA8G9B;AAED,wBAAgB,WAAW,CACzB,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE,YAAY,GAAG,IAAI,GACxB,OAAO,CAQT;AA4DD,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,aAAa,EACvB,OAAO,GAAE,kBAAuB,GAC/B,WAAW,CA8Zb"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/dist/server.js CHANGED
@@ -26,7 +26,7 @@
26
26
  */
27
27
  import { processSecretParams, } from "./agent-definitions/secrets.js";
28
28
  import { verifyJwt } from "./jwt.js";
29
- import { generateSigningKey, importSigningKey, exportSigningKey, buildJwks, verifyJwtLocal, verifyJwtFromIssuer } from "./jwt.js";
29
+ import { generateSigningKey, importSigningKey, exportSigningKey, buildJwks, verifyJwtLocal, verifyJwtFromIssuer, signJwtES256 } from "./jwt.js";
30
30
  // ============================================
31
31
  // HTTP Helpers
32
32
  // ============================================
@@ -257,7 +257,7 @@ function getToolDefinitions() {
257
257
  // Create Server
258
258
  // ============================================
259
259
  export function createAgentServer(registry, options = {}) {
260
- const { port = 3000, hostname = "localhost", basePath = "", cors = true, serverName = "agents-sdk", serverVersion = "1.0.0", secretStore, } = options;
260
+ const { port = 3000, hostname = "localhost", basePath = "", cors = true, serverName = "agents-sdk", serverVersion = "1.0.0", secretStore, oauthIdentityProvider, } = options;
261
261
  // Signing keys for JWKS-based auth
262
262
  const serverSigningKeys = [];
263
263
  // Normalize trustedIssuers to TrustedIssuer objects
@@ -311,6 +311,8 @@ export function createAgentServer(registry, options = {}) {
311
311
  req.metadata = {};
312
312
  req.metadata.scopes = auth.scopes;
313
313
  req.metadata.isRoot = auth.isRoot;
314
+ if (auth.issuer)
315
+ req.metadata.issuer = auth.issuer;
314
316
  }
315
317
  if (auth?.isRoot) {
316
318
  req.callerType = "system";
@@ -362,62 +364,160 @@ export function createAgentServer(registry, options = {}) {
362
364
  // ──────────────────────────────────────────
363
365
  // OAuth2 token handler
364
366
  // ──────────────────────────────────────────
367
+ // Resolve public-facing base URL, respecting reverse proxy headers
368
+ const resolveBaseUrl = (r) => {
369
+ const fwdProto = r.headers.get("x-forwarded-proto");
370
+ const fwdHost = r.headers.get("x-forwarded-host");
371
+ if (fwdProto && fwdHost)
372
+ return `${fwdProto}://${fwdHost}`;
373
+ return new URL(r.url).origin;
374
+ };
365
375
  async function handleOAuthToken(req) {
366
376
  if (!authConfig) {
367
377
  return jsonResponse({ error: "auth_not_configured" }, 404);
368
378
  }
369
379
  const contentType = req.headers.get("Content-Type") ?? "";
370
- let grantType;
371
- let clientId;
372
- let clientSecret;
380
+ let params;
373
381
  if (contentType.includes("application/x-www-form-urlencoded")) {
374
382
  const body = await req.text();
375
- const params = new URLSearchParams(body);
376
- grantType = params.get("grant_type") ?? "";
377
- clientId = params.get("client_id") ?? "";
378
- clientSecret = params.get("client_secret") ?? "";
383
+ const urlParams = new URLSearchParams(body);
384
+ params = Object.fromEntries(urlParams.entries());
379
385
  }
380
386
  else {
381
- const body = (await req.json());
382
- grantType = body.grant_type ?? "";
383
- clientId = body.client_id ?? "";
384
- clientSecret = body.client_secret ?? "";
385
- }
386
- if (grantType !== "client_credentials") {
387
- return jsonResponse({
388
- error: "unsupported_grant_type",
389
- error_description: "Only client_credentials is supported",
390
- }, 400);
391
- }
392
- if (!clientId || !clientSecret) {
393
- return jsonResponse({
394
- error: "invalid_request",
395
- error_description: "Missing client_id or client_secret",
396
- }, 400);
387
+ params = (await req.json());
397
388
  }
398
- try {
399
- const result = await registry.call({
400
- action: "execute_tool",
401
- path: "@auth",
402
- tool: "token",
403
- params: { clientId, clientSecret },
404
- callerType: "system",
405
- });
406
- const tokenResult = result?.result;
407
- if (!tokenResult?.accessToken) {
408
- return jsonResponse({ error: "invalid_client", error_description: "Authentication failed" }, 401);
389
+ const grantType = params.grant_type ?? "";
390
+ // ── jwt_exchange grant: verify foreign JWT, resolve local identity ──
391
+ if (grantType === "jwt_exchange") {
392
+ const assertion = params.assertion ?? "";
393
+ if (!assertion) {
394
+ return jsonResponse({ error: "invalid_request", error_description: "Missing assertion parameter" }, 400);
395
+ }
396
+ try {
397
+ const result = await registry.call({
398
+ action: "execute_tool",
399
+ path: "@auth",
400
+ tool: "exchange_token",
401
+ params: { token: assertion },
402
+ callerType: "system",
403
+ });
404
+ const exchangeResult = result?.result;
405
+ // If the tool call failed, forward the error
406
+ if (result?.success === false) {
407
+ return jsonResponse({ error: "server_error", error_description: result?.error ?? "Exchange tool failed", raw: JSON.stringify(result)?.slice(0, 300) }, 500);
408
+ }
409
+ // ── Reverse registration: if caller is an agent-registry, auto-store connection ──
410
+ try {
411
+ const assertionParts = assertion.split(".");
412
+ if (assertionParts.length === 3) {
413
+ const assertionPayload = JSON.parse(atob(assertionParts[1].replace(/-/g, "+").replace(/_/g, "/")));
414
+ if (assertionPayload.type === "agent-registry" && assertionPayload.iss && false /* disabled: causes infinite loop */) {
415
+ // Find or create @remote-registry agent and store the reverse connection
416
+ const rrAgent = registry.get("@remote-registry") ?? registry.get("/agents/@remote-registry");
417
+ if (rrAgent) {
418
+ const setupTool = rrAgent.tools?.find((t) => t.name === "setup_integration");
419
+ if (setupTool?.execute) {
420
+ try {
421
+ await setupTool.execute({ url: assertionPayload.iss, name: assertionPayload.name ?? "remote-registry" }, { callerId: "system", callerType: "system", tenantId: "default", agentPath: "@remote-registry" });
422
+ console.error(`[jwt_exchange] Reverse connection stored for ${assertionPayload.iss}`);
423
+ }
424
+ catch (setupErr) {
425
+ console.error(`[jwt_exchange] Reverse registration setup failed:`, setupErr);
426
+ }
427
+ }
428
+ else {
429
+ console.error("[jwt_exchange] @remote-registry has no setup_integration tool — reverse registration skipped");
430
+ }
431
+ }
432
+ else {
433
+ console.error("[jwt_exchange] @remote-registry agent not found — reverse registration skipped");
434
+ }
435
+ }
436
+ }
437
+ }
438
+ catch (reverseErr) {
439
+ console.error("[jwt_exchange] Reverse registration check failed:", reverseErr);
440
+ }
441
+ if (!exchangeResult) {
442
+ return jsonResponse({ error: "server_error", error_description: `Exchange returned null: ${JSON.stringify(result)?.slice(0, 300)}` }, 500);
443
+ }
444
+ // User not linked yet — needs OAuth identity linking
445
+ if (exchangeResult.needsAuth) {
446
+ const baseUrl = resolveBaseUrl(req);
447
+ const authorizeUrl = new URL(`${baseUrl}${basePath}/oauth/authorize`);
448
+ authorizeUrl.searchParams.set("token", assertion);
449
+ if (params.redirect_uri) {
450
+ authorizeUrl.searchParams.set("redirect_uri", params.redirect_uri);
451
+ }
452
+ if (params.scope) {
453
+ authorizeUrl.searchParams.set("scope", params.scope);
454
+ }
455
+ return jsonResponse({
456
+ error: "identity_required",
457
+ error_description: "User identity not linked. Redirect to authorize_url to complete linking.",
458
+ authorize_url: authorizeUrl.toString(),
459
+ tenant_id: exchangeResult.tenantId,
460
+ }, 403);
461
+ }
462
+ // User found — sign a local access token
463
+ if (exchangeResult.userId && serverSigningKeys.length > 0) {
464
+ const sigKey = serverSigningKeys[0];
465
+ const token = await signJwtES256({
466
+ sub: exchangeResult.userId,
467
+ name: exchangeResult.userId,
468
+ scopes: ["*"],
469
+ tenantId: exchangeResult.tenantId,
470
+ }, sigKey.privateKey, sigKey.kid, resolveBaseUrl(req), `${authConfig.tokenTtl ?? 3600}s`);
471
+ return jsonResponse({
472
+ access_token: token,
473
+ token_type: "Bearer",
474
+ expires_in: authConfig.tokenTtl ?? 3600,
475
+ user_id: exchangeResult.userId,
476
+ tenant_id: exchangeResult.tenantId,
477
+ });
478
+ }
479
+ return jsonResponse(exchangeResult);
480
+ }
481
+ catch (err) {
482
+ console.error("[oauth] JWT exchange error:", err);
483
+ return jsonResponse({ error: "server_error", error_description: `JWT exchange failed: ${err instanceof Error ? err.message : String(err)}` }, 500);
409
484
  }
410
- return jsonResponse({
411
- access_token: tokenResult.accessToken,
412
- token_type: "Bearer",
413
- expires_in: tokenResult.expiresIn ?? authConfig.tokenTtl,
414
- refresh_token: tokenResult.refreshToken,
415
- });
416
485
  }
417
- catch (err) {
418
- console.error("[oauth] Token error:", err);
419
- return jsonResponse({ error: "server_error", error_description: "Token exchange failed" }, 500);
486
+ // ── client_credentials grant ──
487
+ if (grantType === "client_credentials") {
488
+ const clientId = params.client_id ?? "";
489
+ const clientSecret = params.client_secret ?? "";
490
+ if (!clientId || !clientSecret) {
491
+ return jsonResponse({ error: "invalid_request", error_description: "Missing client_id or client_secret" }, 400);
492
+ }
493
+ try {
494
+ const result = await registry.call({
495
+ action: "execute_tool",
496
+ path: "@auth",
497
+ tool: "token",
498
+ params: { clientId, clientSecret },
499
+ callerType: "system",
500
+ });
501
+ const tokenResult = result?.result;
502
+ if (!tokenResult?.accessToken) {
503
+ return jsonResponse({ error: "invalid_client", error_description: "Authentication failed" }, 401);
504
+ }
505
+ return jsonResponse({
506
+ access_token: tokenResult.accessToken,
507
+ token_type: "Bearer",
508
+ expires_in: tokenResult.expiresIn ?? authConfig.tokenTtl,
509
+ refresh_token: tokenResult.refreshToken,
510
+ });
511
+ }
512
+ catch (err) {
513
+ console.error("[oauth] Token error:", err);
514
+ return jsonResponse({ error: "server_error", error_description: "Token exchange failed" }, 500);
515
+ }
420
516
  }
517
+ return jsonResponse({
518
+ error: "unsupported_grant_type",
519
+ error_description: "Supported grant types: client_credentials, jwt_exchange",
520
+ }, 400);
421
521
  }
422
522
  // ──────────────────────────────────────────
423
523
  // Main fetch handler
@@ -458,11 +558,68 @@ export function createAgentServer(registry, options = {}) {
458
558
  const result = await handleJsonRpc(body, effectiveAuth);
459
559
  return cors ? addCors(jsonResponse(result)) : jsonResponse(result);
460
560
  }
461
- // ── POST /oauth/token → OAuth2 client_credentials ──
561
+ // ── POST /oauth/token → OAuth2 token exchange ──
462
562
  if (path === "/oauth/token" && req.method === "POST") {
463
563
  const res = await handleOAuthToken(req);
464
564
  return cors ? addCors(res) : res;
465
565
  }
566
+ // ── GET /oauth/authorize → Identity linking redirect (browser flow) ──
567
+ if (path === "/oauth/authorize" && req.method === "GET") {
568
+ if (!oauthIdentityProvider) {
569
+ const res = jsonResponse({ error: "not_configured", error_description: "No OAuth identity provider configured" }, 404);
570
+ return cors ? addCors(res) : res;
571
+ }
572
+ const url = new URL(req.url);
573
+ const token = url.searchParams.get("token") ?? "";
574
+ const redirectUri = url.searchParams.get("redirect_uri") ?? "";
575
+ if (!token) {
576
+ const res = jsonResponse({ error: "invalid_request", error_description: "Missing token parameter" }, 400);
577
+ return cors ? addCors(res) : res;
578
+ }
579
+ // Verify the JWT against trusted issuers (from store, falling back to config)
580
+ let claims = null;
581
+ const storeIssuers = authConfig?.store?.listTrustedIssuers
582
+ ? await authConfig.store.listTrustedIssuers()
583
+ : [];
584
+ const configIssuerUrls = configTrustedIssuers.map(i => typeof i === "string" ? i : i.issuer);
585
+ const allIssuerUrls = [...new Set([...storeIssuers, ...configIssuerUrls])];
586
+ for (const issuerUrl of allIssuerUrls) {
587
+ try {
588
+ const result = await verifyJwtFromIssuer(token, issuerUrl);
589
+ if (result) {
590
+ claims = result;
591
+ break;
592
+ }
593
+ }
594
+ catch { /* try next issuer */ }
595
+ }
596
+ if (!claims) {
597
+ const res = jsonResponse({ error: "invalid_token", error_description: "JWT verification failed against all trusted issuers" }, 401);
598
+ return cors ? addCors(res) : res;
599
+ }
600
+ const baseUrl = resolveBaseUrl(req);
601
+ const scope = url.searchParams.get("scope") ?? undefined;
602
+ const res = await oauthIdentityProvider.authorize(req, {
603
+ token,
604
+ claims,
605
+ redirectUri,
606
+ baseUrl: baseUrl + basePath,
607
+ scope,
608
+ });
609
+ return cors ? addCors(res) : res;
610
+ }
611
+ // ── GET /oauth/callback → Identity linking callback ──
612
+ if (path === "/oauth/callback" && req.method === "GET") {
613
+ if (!oauthIdentityProvider) {
614
+ const res = jsonResponse({ error: "not_configured", error_description: "No OAuth identity provider configured" }, 404);
615
+ return cors ? addCors(res) : res;
616
+ }
617
+ const baseUrl = resolveBaseUrl(req);
618
+ const res = await oauthIdentityProvider.callback(req, {
619
+ baseUrl: baseUrl + basePath,
620
+ });
621
+ return cors ? addCors(res) : res;
622
+ }
466
623
  // ── GET /health → Health check ──
467
624
  if (path === "/health" && req.method === "GET") {
468
625
  const res = jsonResponse({ status: "ok", agents: registry.listPaths() });
@@ -478,14 +635,15 @@ export function createAgentServer(registry, options = {}) {
478
635
  }
479
636
  // ── GET /.well-known/configuration → Server discovery ──
480
637
  if (path === "/.well-known/configuration" && req.method === "GET") {
481
- const baseUrl = new URL(req.url).origin;
638
+ const baseUrl = resolveBaseUrl(req);
482
639
  const res = jsonResponse({
483
640
  issuer: baseUrl,
484
641
  jwks_uri: `${baseUrl}/.well-known/jwks.json`,
485
642
  token_endpoint: `${baseUrl}/oauth/token`,
486
643
  agents_endpoint: `${baseUrl}/list`,
487
644
  call_endpoint: baseUrl,
488
- supported_grant_types: ["client_credentials"],
645
+ supported_grant_types: ["client_credentials", "jwt_exchange"],
646
+ authorization_endpoint: `${baseUrl}/oauth/authorize`,
489
647
  agents: registry.listPaths(),
490
648
  });
491
649
  return cors ? addCors(res) : res;
@@ -548,12 +706,12 @@ export function createAgentServer(registry, options = {}) {
548
706
  return {
549
707
  url: null,
550
708
  registry,
551
- async start() {
552
- // Load or generate signing key for JWKS
553
- if (options.signingKey) {
709
+ async initKeys() {
710
+ // Load or generate signing keys (without starting Bun.serve)
711
+ if (options.signingKey && serverSigningKeys.length === 0) {
554
712
  serverSigningKeys.push(options.signingKey);
555
713
  }
556
- else if (authConfig?.store?.getSigningKeys) {
714
+ else if (authConfig?.store?.getSigningKeys && serverSigningKeys.length === 0) {
557
715
  const stored = await authConfig.store.getSigningKeys() ?? [];
558
716
  for (const exported of stored) {
559
717
  serverSigningKeys.push(await importSigningKey(exported));
@@ -566,6 +724,9 @@ export function createAgentServer(registry, options = {}) {
566
724
  await authConfig.store.storeSigningKey(await exportSigningKey(key));
567
725
  }
568
726
  }
727
+ },
728
+ async start() {
729
+ await this.initKeys();
569
730
  serverInstance = Bun.serve({
570
731
  port,
571
732
  hostname,
@@ -582,6 +743,21 @@ export function createAgentServer(registry, options = {}) {
582
743
  }
583
744
  },
584
745
  fetch,
746
+ async signJwt(claims) {
747
+ if (serverSigningKeys.length === 0) {
748
+ throw new Error('No signing keys available. Call start() or initKeys() first.');
749
+ }
750
+ const key = serverSigningKeys[0];
751
+ return signJwtES256({ sub: 'system', name: 'atlas-os', scopes: ['*'], ...claims }, key.privateKey, key.kid, options.serverName ?? 'agents-sdk', '1h');
752
+ },
753
+ addTrustedIssuer(issuerUrl, scopes) {
754
+ // Avoid duplicates
755
+ const existing = configTrustedIssuers.find(i => i.issuer === issuerUrl);
756
+ if (!existing) {
757
+ configTrustedIssuers.push({ issuer: issuerUrl, scopes: scopes ?? ['*'] });
758
+ console.error(`[agent-server] Added trusted issuer: ${issuerUrl}`);
759
+ }
760
+ },
585
761
  };
586
762
  }
587
763
  //# sourceMappingURL=server.js.map