@kya-os/mcp-i-cloudflare 1.5.1-canary.6 → 1.5.1

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 (40) hide show
  1. package/dist/adapter.d.ts +8 -0
  2. package/dist/adapter.d.ts.map +1 -1
  3. package/dist/adapter.js +102 -87
  4. package/dist/adapter.js.map +1 -1
  5. package/dist/constants/storage-keys.d.ts +89 -0
  6. package/dist/constants/storage-keys.d.ts.map +1 -0
  7. package/dist/constants/storage-keys.js +142 -0
  8. package/dist/constants/storage-keys.js.map +1 -0
  9. package/dist/index.d.ts +7 -3
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +9 -3
  12. package/dist/index.js.map +1 -1
  13. package/dist/runtime/oauth-handler.d.ts +6 -0
  14. package/dist/runtime/oauth-handler.d.ts.map +1 -1
  15. package/dist/runtime/oauth-handler.js +96 -21
  16. package/dist/runtime/oauth-handler.js.map +1 -1
  17. package/dist/services/consent-page-renderer.d.ts +5 -2
  18. package/dist/services/consent-page-renderer.d.ts.map +1 -1
  19. package/dist/services/consent-page-renderer.js +29 -8
  20. package/dist/services/consent-page-renderer.js.map +1 -1
  21. package/dist/services/consent.service.d.ts +52 -0
  22. package/dist/services/consent.service.d.ts.map +1 -1
  23. package/dist/services/consent.service.js +451 -99
  24. package/dist/services/consent.service.js.map +1 -1
  25. package/dist/services/delegation.service.d.ts.map +1 -1
  26. package/dist/services/delegation.service.js +54 -19
  27. package/dist/services/delegation.service.js.map +1 -1
  28. package/dist/services/oauth-security.service.d.ts +92 -0
  29. package/dist/services/oauth-security.service.d.ts.map +1 -0
  30. package/dist/services/oauth-security.service.js +260 -0
  31. package/dist/services/oauth-security.service.js.map +1 -0
  32. package/dist/services/rate-limit.service.d.ts +59 -0
  33. package/dist/services/rate-limit.service.d.ts.map +1 -0
  34. package/dist/services/rate-limit.service.js +146 -0
  35. package/dist/services/rate-limit.service.js.map +1 -0
  36. package/dist/utils/day0-config.d.ts +51 -0
  37. package/dist/utils/day0-config.d.ts.map +1 -0
  38. package/dist/utils/day0-config.js +72 -0
  39. package/dist/utils/day0-config.js.map +1 -0
  40. package/package.json +3 -3
@@ -9,19 +9,174 @@
9
9
  import { ConsentConfigService } from "./consent-config.service";
10
10
  import { ConsentPageRenderer } from "./consent-page-renderer";
11
11
  import { DEFAULT_AGENTSHIELD_URL, DEFAULT_SESSION_CACHE_TTL, } from "../constants";
12
+ import { STORAGE_KEYS } from "../constants/storage-keys";
13
+ import { loadDay0Config, getDelegationFieldName } from "../utils/day0-config";
12
14
  import { validateConsentApprovalRequest, } from "@kya-os/contracts/consent";
13
15
  import { AGENTSHIELD_ENDPOINTS, createDelegationAPIResponseSchema, createDelegationResponseSchema, } from "@kya-os/contracts/agentshield-api";
16
+ import { UserDidManager } from "@kya-os/mcp-i-core";
17
+ import { WebCryptoProvider } from "../providers/crypto";
14
18
  export class ConsentService {
15
19
  configService;
16
20
  renderer;
17
21
  env;
18
22
  runtime;
23
+ userDidManager; // Cached instance for consistent DID generation
19
24
  constructor(env, runtime) {
20
25
  this.env = env;
21
26
  this.runtime = runtime;
22
27
  this.configService = new ConsentConfigService(env);
23
28
  this.renderer = new ConsentPageRenderer();
24
29
  }
30
+ /**
31
+ * Get or generate User DID for a session
32
+ *
33
+ * Phase 4 PR #1: Generates ephemeral DIDs for sessions
34
+ * Phase 4 PR #3: Checks OAuth mappings for persistent DIDs
35
+ *
36
+ * @param sessionId - Session ID
37
+ * @param oauthIdentity - Optional OAuth provider identity
38
+ * @returns User DID (did:key format)
39
+ */
40
+ async getUserDidForSession(sessionId, oauthIdentity) {
41
+ // If OAuth identity provided, check for existing mapping first
42
+ if (oauthIdentity && this.env.DELEGATION_STORAGE) {
43
+ try {
44
+ const oauthKey = STORAGE_KEYS.oauthIdentity(oauthIdentity.provider, oauthIdentity.subject);
45
+ const mappedUserDid = await this.env.DELEGATION_STORAGE.get(oauthKey, "text");
46
+ if (mappedUserDid) {
47
+ console.log("[ConsentService] Found persistent User DID from OAuth mapping");
48
+ return mappedUserDid;
49
+ }
50
+ }
51
+ catch (error) {
52
+ console.warn("[ConsentService] Failed to check OAuth mapping:", error);
53
+ // Continue with ephemeral DID generation
54
+ }
55
+ }
56
+ // Continue with existing ephemeral DID generation logic
57
+ if (!this.env.DELEGATION_STORAGE) {
58
+ // No storage - use cached UserDidManager instance for consistent DID generation
59
+ if (!this.userDidManager) {
60
+ this.userDidManager = new UserDidManager({
61
+ crypto: new WebCryptoProvider(),
62
+ });
63
+ }
64
+ return await this.userDidManager.getOrCreateUserDid(sessionId);
65
+ }
66
+ const sessionKey = STORAGE_KEYS.session(sessionId);
67
+ // Try session cache first
68
+ try {
69
+ const sessionData = (await this.env.DELEGATION_STORAGE.get(sessionKey, "json"));
70
+ if (sessionData?.userDid) {
71
+ return sessionData.userDid;
72
+ }
73
+ }
74
+ catch (error) {
75
+ console.warn("[ConsentService] Failed to read session cache:", error);
76
+ }
77
+ // Generate ephemeral DID using cached UserDidManager instance
78
+ if (!this.userDidManager) {
79
+ this.userDidManager = new UserDidManager({
80
+ crypto: new WebCryptoProvider(),
81
+ storage: {
82
+ get: async (key) => {
83
+ try {
84
+ const data = await this.env.DELEGATION_STORAGE.get(`userDid:${key}`, "text");
85
+ return data || null;
86
+ }
87
+ catch {
88
+ return null;
89
+ }
90
+ },
91
+ set: async (key, value, ttl) => {
92
+ await this.env.DELEGATION_STORAGE.put(`userDid:${key}`, value, {
93
+ expirationTtl: ttl || DEFAULT_SESSION_CACHE_TTL,
94
+ });
95
+ },
96
+ delete: async (key) => {
97
+ await this.env.DELEGATION_STORAGE.delete(`userDid:${key}`);
98
+ },
99
+ },
100
+ });
101
+ }
102
+ const userDid = await this.userDidManager.getOrCreateUserDid(sessionId);
103
+ // Cache in session storage
104
+ try {
105
+ const existingSession = (await this.env.DELEGATION_STORAGE.get(sessionKey, "json"));
106
+ await this.env.DELEGATION_STORAGE.put(sessionKey, JSON.stringify({
107
+ ...existingSession,
108
+ userDid,
109
+ }), { expirationTtl: DEFAULT_SESSION_CACHE_TTL });
110
+ }
111
+ catch (error) {
112
+ console.warn("[ConsentService] Failed to cache userDid in session:", error);
113
+ // Non-fatal - continue with generated DID
114
+ }
115
+ return userDid;
116
+ }
117
+ /**
118
+ * Link OAuth identity to User DID
119
+ *
120
+ * Maps OAuth provider identity (provider + subject) to a persistent User DID.
121
+ * If an ephemeral DID exists for the session, it becomes persistent.
122
+ *
123
+ * Phase 4 PR #3: OAuth Identity Linking
124
+ *
125
+ * @param oauthIdentity - OAuth provider identity
126
+ * @param sessionId - Current session ID (for ephemeral DID lookup)
127
+ * @returns Persistent User DID
128
+ */
129
+ async linkOAuthToUserDid(oauthIdentity, sessionId) {
130
+ if (!this.env.DELEGATION_STORAGE) {
131
+ // No storage - can't persist mapping, return ephemeral DID
132
+ console.warn("[ConsentService] No storage available for OAuth linking");
133
+ return await this.getUserDidForSession(sessionId);
134
+ }
135
+ const oauthKey = STORAGE_KEYS.oauthIdentity(oauthIdentity.provider, oauthIdentity.subject);
136
+ // Check if OAuth identity already mapped
137
+ try {
138
+ const existingUserDid = await this.env.DELEGATION_STORAGE.get(oauthKey, "text");
139
+ if (existingUserDid) {
140
+ console.log("[ConsentService] OAuth identity already mapped:", {
141
+ provider: oauthIdentity.provider,
142
+ subject: oauthIdentity.subject.substring(0, 20) + "...",
143
+ userDid: existingUserDid.substring(0, 20) + "...",
144
+ });
145
+ return existingUserDid;
146
+ }
147
+ }
148
+ catch (error) {
149
+ console.warn("[ConsentService] Failed to check OAuth mapping:", error);
150
+ // Continue to create new mapping
151
+ }
152
+ // Get/create User DID for session (may be ephemeral)
153
+ const userDid = await this.getUserDidForSession(sessionId);
154
+ // Store OAuth identity mapping (persistent - 90 days)
155
+ try {
156
+ await this.env.DELEGATION_STORAGE.put(oauthKey, userDid, {
157
+ expirationTtl: 90 * 24 * 60 * 60, // 90 days
158
+ });
159
+ // Also store full OAuth identity info for reference
160
+ const oauthIdentityKey = STORAGE_KEYS.userDid(oauthIdentity.provider, oauthIdentity.subject);
161
+ await this.env.DELEGATION_STORAGE.put(oauthIdentityKey, JSON.stringify(oauthIdentity), {
162
+ expirationTtl: 90 * 24 * 60 * 60, // 90 days
163
+ });
164
+ console.log("[ConsentService] OAuth identity linked to User DID:", {
165
+ provider: oauthIdentity.provider,
166
+ subject: oauthIdentity.subject.substring(0, 20) + "...",
167
+ userDid: userDid.substring(0, 20) + "...",
168
+ });
169
+ }
170
+ catch (error) {
171
+ console.error("[ConsentService] Failed to store OAuth mapping:", error);
172
+ // Non-fatal - continue with User DID
173
+ }
174
+ // Note: Ephemeral → persistent migration happens automatically
175
+ // The ephemeral DID becomes persistent when linked to OAuth identity
176
+ // Existing delegations with ephemeral DID will continue to work
177
+ // New delegations will use the persistent DID
178
+ return userDid;
179
+ }
25
180
  /**
26
181
  * Handle consent requests
27
182
  *
@@ -135,8 +290,40 @@ export class ConsentService {
135
290
  customFields: consentConfig.customFields,
136
291
  autoClose: consentConfig.ui?.autoClose,
137
292
  };
138
- // Render page
139
- const html = this.renderer.render(pageConfig);
293
+ // Phase 4 PR #4: Extract OAuth identity from cookie (server-side)
294
+ let oauthIdentity = undefined;
295
+ try {
296
+ const cookieHeader = request.headers.get("Cookie");
297
+ if (cookieHeader) {
298
+ const cookies = cookieHeader.split("; ").map((c) => c.trim());
299
+ const oauthCookie = cookies.find((c) => c.startsWith("oauth_identity="));
300
+ if (oauthCookie) {
301
+ // Extract cookie value correctly - handle values that may contain '=' characters
302
+ // Use indexOf to find first '=' and take everything after it
303
+ const equalsIndex = oauthCookie.indexOf("=");
304
+ const cookieValue = equalsIndex >= 0
305
+ ? oauthCookie.substring(equalsIndex + 1)
306
+ : "";
307
+ // Validate it's valid JSON before passing to client
308
+ try {
309
+ const parsed = JSON.parse(decodeURIComponent(cookieValue));
310
+ // Basic validation - ensure it has required fields
311
+ if (parsed && parsed.provider && parsed.subject) {
312
+ oauthIdentity = parsed;
313
+ }
314
+ }
315
+ catch (parseError) {
316
+ console.warn("[ConsentService] Invalid OAuth cookie format:", parseError);
317
+ }
318
+ }
319
+ }
320
+ }
321
+ catch (error) {
322
+ console.warn("[ConsentService] Failed to extract OAuth cookie:", error);
323
+ // Non-fatal - continue without OAuth identity
324
+ }
325
+ // Render page with OAuth identity (if available)
326
+ const html = this.renderer.render(pageConfig, oauthIdentity);
140
327
  return new Response(html, {
141
328
  status: 200,
142
329
  headers: {
@@ -250,78 +437,41 @@ export class ConsentService {
250
437
  };
251
438
  }
252
439
  try {
253
- // Create delegation request per AgentShield API schema
254
- // Schema: https://github.com/modelcontextprotocol-identity/mcp-i/blob/main/packages/contracts/src/agentshield-api/schemas.ts
255
- // AgentShield implementation: apps/web/lib/bouncer/validators/schemas.ts
256
- //
257
- // Required fields:
258
- // - agent_did: string (DID format)
259
- // - scopes: string[] (at least one scope)
260
- //
261
- // Optional fields:
262
- // - expires_in_days: number (1-365, defaults to project default if not provided)
263
- // - expires_at: string (ISO 8601 date string, alternative to expires_in_days)
264
- // NOTE: expires_in_days and expires_at are mutually exclusive - use one or the other
265
- // - session_id: string (optional session identifier)
266
- // - project_id: string (optional project identifier, also comes from API key context)
267
- // - user_id: string (optional user identifier)
268
- // - user_identifier: string (optional user identifier)
269
- // - agent_name: string (optional agent name)
270
- // - constraints: object (optional constraints)
271
- // - custom_fields: object (optional custom fields)
272
- // - metadata: object (optional metadata)
273
- const expiresInDays = 7; // Default to 7 days
274
- const delegationRequest = {
275
- agent_did: request.agent_did,
276
- scopes: request.scopes,
277
- expires_in_days: expiresInDays,
278
- // NOTE: expires_at and expires_in_days are mutually exclusive.
279
- // We use expires_in_days for simplicity. Do not include both fields.
280
- };
281
- // Include session_id if provided
282
- if (request.session_id) {
283
- delegationRequest.session_id = request.session_id;
284
- }
285
- // Include project_id if provided
286
- if (request.project_id) {
287
- delegationRequest.project_id = request.project_id;
288
- }
289
- // Include custom_fields if provided (send as custom_fields, not metadata)
290
- if (request.customFields &&
291
- Object.keys(request.customFields).length > 0) {
292
- delegationRequest.custom_fields = request.customFields;
440
+ // Load Day0 configuration to determine field name and API capabilities
441
+ await loadDay0Config(this.env.DELEGATION_STORAGE);
442
+ const fieldName = await getDelegationFieldName(this.env.DELEGATION_STORAGE);
443
+ // Get userDID from session or generate new ephemeral DID
444
+ // Phase 4 PR #3: Use OAuth identity if provided in approval request
445
+ let userDid;
446
+ if (this.env.DELEGATION_STORAGE && request.session_id) {
447
+ try {
448
+ // Pass OAuth identity if available in approval request
449
+ userDid = await this.getUserDidForSession(request.session_id, request.oauth_identity);
450
+ }
451
+ catch (error) {
452
+ console.warn("[ConsentService] Failed to get/generate userDid:", error);
453
+ // Continue without userDid - delegation will use ephemeral placeholder
454
+ }
293
455
  }
456
+ const expiresInDays = 7; // Default to 7 days
457
+ // Build delegation request with error-based format detection
458
+ // Try full format first, fallback to simplified format on error
459
+ const delegationRequest = await this.buildDelegationRequest(request, userDid, expiresInDays, fieldName);
294
460
  console.log("[ConsentService] Creating delegation:", {
295
461
  url: `${agentShieldUrl}${AGENTSHIELD_ENDPOINTS.DELEGATIONS_CREATE}`,
296
462
  agentDid: request.agent_did.substring(0, 20) + "...",
297
463
  scopes: request.scopes,
298
464
  expiresInDays,
299
465
  hasApiKey: !!apiKey,
300
- // Note: project_id comes from API key, not request body
301
- });
302
- const response = await fetch(`${agentShieldUrl}${AGENTSHIELD_ENDPOINTS.DELEGATIONS_CREATE}`, {
303
- method: "POST",
304
- headers: {
305
- Authorization: `Bearer ${apiKey}`,
306
- "Content-Type": "application/json",
307
- },
308
- body: JSON.stringify(delegationRequest),
466
+ fieldName,
467
+ hasUserDid: !!userDid,
309
468
  });
310
- if (!response.ok) {
311
- const errorText = await response.text();
312
- console.error("[ConsentService] Delegation creation failed:", {
313
- status: response.status,
314
- statusText: response.statusText,
315
- error: errorText,
316
- requestBody: JSON.stringify(delegationRequest, null, 2),
317
- });
318
- return {
319
- success: false,
320
- error: `API error: ${response.status}`,
321
- error_code: "api_error",
322
- };
469
+ // Error-based format detection: try request format, fallback on error
470
+ const response = await this.tryAPICall(agentShieldUrl, apiKey, delegationRequest);
471
+ if (!response.success) {
472
+ return response;
323
473
  }
324
- const responseData = (await response.json());
474
+ const responseData = response.data;
325
475
  // Canonical format per @kya-os/contracts/agentshield-api:
326
476
  // Wrapped: { success: true, data: { delegation_id: string, agent_did: string, ... } }
327
477
  // Unwrapped: { delegation_id: string, agent_did: string, ... }
@@ -415,38 +565,16 @@ export class ConsentService {
415
565
  try {
416
566
  // Default TTL: 7 days (same as delegation expiration)
417
567
  const ttl = 7 * 24 * 60 * 60; // 7 days in seconds
418
- // TODO: Store tokens with userDID when available for proper multi-user support
419
- // Currently, we store tokens with agent-scoped keys which can cause conflicts
420
- // when multiple users delegate to the same agent. The ideal key structure should be:
421
- // - `user:${userDid}:agent:${agentDid}:delegation` (user+agent scoped - preferred)
422
- // - `agent:${agentDid}:delegation` (agent-scoped fallback for single-user agents)
423
- //
424
- // To implement this properly:
425
- // 1. Capture userDID from consent approval request (from OAuth callback or session)
426
- // 2. Store tokens with userDID in the key: `user:${userDid}:agent:${agentDid}:delegation`
427
- // 3. Also store with agent-scoped key for backward compatibility
428
- //
429
- // Note: The consent approval request currently doesn't include userDID, so we need to:
430
- // - Extract userDID from OAuth callback (if using OAuth flow)
431
- // - Or extract userDID from session (if session has userDID)
432
- // - Or add userDID to ConsentApprovalRequest interface
433
- const userDid = undefined; // TODO: Get userDID from approval request or OAuth callback
434
- // Store using agent DID (primary, survives session changes)
435
- // WARNING: This is shared across all users - if multiple users delegate to the same
436
- // agent, each delegation will overwrite the previous one. This is acceptable for
437
- // single-user agents, but multi-user agents should use user+agent scoped keys.
438
- const agentKey = `agent:${agentDid}:delegation`;
439
- await delegationStorage.put(agentKey, token, {
440
- expirationTtl: ttl,
441
- });
442
- console.log("[ConsentService] ✅ Token stored with agent DID:", {
443
- key: agentKey,
444
- ttl,
445
- delegationId,
446
- });
447
- // Store using user+agent DID if userDID is available (preferred for multi-user scenarios)
568
+ // Get userDID from approval request or session (Phase 4)
569
+ // For now, try to extract from request or session cache
570
+ let userDid;
571
+ // Try to get userDID from session cache
572
+ const sessionKey = STORAGE_KEYS.session(sessionId);
573
+ const sessionData = (await delegationStorage.get(sessionKey, "json"));
574
+ userDid = sessionData?.userDid;
575
+ // Primary: User+Agent scoped (no conflicts) - Phase 4
448
576
  if (userDid) {
449
- const userAgentKey = `user:${userDid}:agent:${agentDid}:delegation`;
577
+ const userAgentKey = STORAGE_KEYS.delegation(userDid, agentDid);
450
578
  await delegationStorage.put(userAgentKey, token, {
451
579
  expirationTtl: ttl,
452
580
  });
@@ -456,15 +584,31 @@ export class ConsentService {
456
584
  delegationId,
457
585
  });
458
586
  }
459
- // Store using session ID (secondary cache, shorter TTL for performance)
460
- const sessionKey = `session:${sessionId}`;
461
- await delegationStorage.put(sessionKey, token, {
587
+ // Backward compatibility: Agent-only key (24 hour TTL)
588
+ const legacyKey = STORAGE_KEYS.legacyDelegation(agentDid);
589
+ await delegationStorage.put(legacyKey, token, {
590
+ expirationTtl: 24 * 60 * 60, // 24 hours only
591
+ });
592
+ console.log("[ConsentService] ✅ Token stored with legacy agent key:", {
593
+ key: legacyKey,
594
+ ttl: 24 * 60 * 60,
595
+ delegationId,
596
+ });
597
+ // Session cache for fast lookup (shorter TTL for performance)
598
+ const sessionDataToStore = {
599
+ userDid,
600
+ agentDid,
601
+ delegationToken: token,
602
+ cachedAt: Date.now(),
603
+ };
604
+ await delegationStorage.put(sessionKey, JSON.stringify(sessionDataToStore), {
462
605
  expirationTtl: Math.min(ttl, DEFAULT_SESSION_CACHE_TTL),
463
606
  });
464
607
  console.log("[ConsentService] ✅ Token cached for session:", {
465
608
  key: sessionKey,
466
609
  ttl: Math.min(ttl, DEFAULT_SESSION_CACHE_TTL),
467
610
  sessionId,
611
+ userDid,
468
612
  });
469
613
  }
470
614
  catch (error) {
@@ -506,5 +650,213 @@ export class ConsentService {
506
650
  },
507
651
  });
508
652
  }
653
+ /**
654
+ * Build delegation request with error-based format detection
655
+ *
656
+ * Uses Day0 config to determine field name and includes issuerDid when available.
657
+ */
658
+ async buildDelegationRequest(request, userDid, expiresInDays, fieldName) {
659
+ const baseRequest = {
660
+ agent_did: request.agent_did,
661
+ scopes: request.scopes,
662
+ expires_in_days: expiresInDays,
663
+ };
664
+ // Include session_id if provided
665
+ if (request.session_id) {
666
+ baseRequest.session_id = request.session_id;
667
+ }
668
+ // Include project_id if provided
669
+ if (request.project_id) {
670
+ baseRequest.project_id = request.project_id;
671
+ }
672
+ // Check cached format preference
673
+ const cacheKey = STORAGE_KEYS.formatPreference();
674
+ let cachedFormat = null;
675
+ if (this.env.DELEGATION_STORAGE) {
676
+ try {
677
+ const cached = (await this.env.DELEGATION_STORAGE.get(cacheKey, "json"));
678
+ if (cached &&
679
+ cached.timestamp &&
680
+ Date.now() - cached.timestamp < 3600000) {
681
+ cachedFormat = cached.format;
682
+ }
683
+ }
684
+ catch {
685
+ // Ignore cache errors
686
+ }
687
+ }
688
+ // If we have a cached preference, use it directly
689
+ if (cachedFormat === "full") {
690
+ return this.buildFullFormatRequest(request, userDid, expiresInDays);
691
+ }
692
+ else if (cachedFormat === "simplified") {
693
+ return this.buildSimplifiedFormatRequest(request, userDid, expiresInDays, fieldName);
694
+ }
695
+ // No cache - return request that will be tried with error-based detection
696
+ return {
697
+ _tryFormats: true,
698
+ fullFormat: await this.buildFullFormatRequest(request, userDid, expiresInDays),
699
+ simplifiedFormat: this.buildSimplifiedFormatRequest(request, userDid, expiresInDays, fieldName),
700
+ };
701
+ }
702
+ /**
703
+ * Build full DelegationRecord format request (future format)
704
+ */
705
+ async buildFullFormatRequest(request, userDid, expiresInDays) {
706
+ const notAfter = Date.now() + expiresInDays * 24 * 60 * 60 * 1000;
707
+ return {
708
+ delegation: {
709
+ id: crypto.randomUUID(),
710
+ issuerDid: userDid || "did:key:z6MkEphemeral", // Use ephemeral if no userDid
711
+ subjectDid: request.agent_did,
712
+ constraints: {
713
+ scopes: request.scopes,
714
+ notAfter,
715
+ notBefore: Date.now(),
716
+ },
717
+ status: "active",
718
+ createdAt: Date.now(),
719
+ },
720
+ };
721
+ }
722
+ /**
723
+ * Build simplified format request with proper field name
724
+ */
725
+ buildSimplifiedFormatRequest(request, userDid, expiresInDays, fieldName) {
726
+ const simplifiedRequest = {
727
+ agent_did: request.agent_did,
728
+ scopes: request.scopes,
729
+ expires_in_days: expiresInDays,
730
+ };
731
+ if (request.session_id) {
732
+ simplifiedRequest.session_id = request.session_id;
733
+ }
734
+ if (request.project_id) {
735
+ simplifiedRequest.project_id = request.project_id;
736
+ }
737
+ // Use the correct field name (metadata or custom_fields) from Day0 config
738
+ if (userDid) {
739
+ simplifiedRequest[fieldName] = {
740
+ issuer_did: userDid,
741
+ subject_did: request.agent_did,
742
+ format_version: "simplified_v1",
743
+ };
744
+ }
745
+ // Include custom_fields from request if provided
746
+ if (request.customFields && Object.keys(request.customFields).length > 0) {
747
+ simplifiedRequest.custom_fields = {
748
+ ...(simplifiedRequest.custom_fields || {}),
749
+ ...request.customFields,
750
+ };
751
+ }
752
+ return simplifiedRequest;
753
+ }
754
+ /**
755
+ * Try API call with error-based format detection
756
+ */
757
+ async tryAPICall(agentShieldUrl, apiKey, request) {
758
+ // Handle format detection
759
+ if (request._tryFormats && request.fullFormat && request.simplifiedFormat) {
760
+ // Try full format first
761
+ const fullResponse = await this.makeAPICall(agentShieldUrl, apiKey, request.fullFormat);
762
+ if (fullResponse.success ||
763
+ fullResponse.error_code !== "validation_error") {
764
+ // Full format worked or failed for non-format reasons
765
+ await this.cacheFormatPreference("full");
766
+ return fullResponse;
767
+ }
768
+ // Full format failed with validation error, try simplified
769
+ console.log("[ConsentService] Full format failed, trying simplified format...");
770
+ const simplifiedResponse = await this.makeAPICall(agentShieldUrl, apiKey, request.simplifiedFormat);
771
+ if (simplifiedResponse.success) {
772
+ await this.cacheFormatPreference("simplified");
773
+ }
774
+ return simplifiedResponse;
775
+ }
776
+ // Direct call (format already determined)
777
+ return this.makeAPICall(agentShieldUrl, apiKey, request);
778
+ }
779
+ /**
780
+ * Make API call and parse response
781
+ */
782
+ async makeAPICall(agentShieldUrl, apiKey, requestBody) {
783
+ try {
784
+ const response = await fetch(`${agentShieldUrl}${AGENTSHIELD_ENDPOINTS.DELEGATIONS_CREATE}`, {
785
+ method: "POST",
786
+ headers: {
787
+ Authorization: `Bearer ${apiKey}`,
788
+ "Content-Type": "application/json",
789
+ "X-Request-ID": crypto.randomUUID(),
790
+ },
791
+ body: JSON.stringify(requestBody),
792
+ });
793
+ const responseText = await response.text();
794
+ let responseData;
795
+ try {
796
+ responseData = JSON.parse(responseText);
797
+ }
798
+ catch {
799
+ responseData = responseText;
800
+ }
801
+ // Check for validation error specifically
802
+ if (response.status === 400) {
803
+ const errorMessage = responseData
804
+ ?.error?.message ||
805
+ responseData?.message ||
806
+ "Validation failed";
807
+ if (errorMessage.includes("format") ||
808
+ errorMessage.includes("schema") ||
809
+ errorMessage.includes("invalid")) {
810
+ return {
811
+ success: false,
812
+ error: errorMessage,
813
+ error_code: "validation_error",
814
+ };
815
+ }
816
+ }
817
+ if (!response.ok) {
818
+ const errorData = responseData;
819
+ return {
820
+ success: false,
821
+ error: errorData.error?.message ||
822
+ errorData.message ||
823
+ "API request failed",
824
+ error_code: errorData.error?.code || "api_error",
825
+ };
826
+ }
827
+ return {
828
+ success: true,
829
+ data: responseData,
830
+ };
831
+ }
832
+ catch (error) {
833
+ console.error("[ConsentService] API call failed:", error);
834
+ return {
835
+ success: false,
836
+ error: error instanceof Error ? error.message : "Network request failed",
837
+ error_code: "network_error",
838
+ };
839
+ }
840
+ }
841
+ /**
842
+ * Cache successful format preference
843
+ */
844
+ async cacheFormatPreference(format) {
845
+ if (!this.env.DELEGATION_STORAGE)
846
+ return;
847
+ const cacheKey = STORAGE_KEYS.formatPreference();
848
+ try {
849
+ await this.env.DELEGATION_STORAGE.put(cacheKey, JSON.stringify({
850
+ format,
851
+ timestamp: Date.now(),
852
+ }), {
853
+ expirationTtl: 3600, // 1 hour
854
+ });
855
+ console.log(`[ConsentService] Cached format preference: ${format}`);
856
+ }
857
+ catch (error) {
858
+ console.warn("[ConsentService] Failed to cache format preference:", error);
859
+ }
860
+ }
509
861
  }
510
862
  //# sourceMappingURL=consent.service.js.map