@kya-os/mcp-i-cloudflare 1.5.8-canary.4 → 1.5.8-canary.40

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 (67) hide show
  1. package/README.md +130 -0
  2. package/dist/__tests__/e2e/test-config.d.ts +37 -0
  3. package/dist/__tests__/e2e/test-config.d.ts.map +1 -0
  4. package/dist/__tests__/e2e/test-config.js +62 -0
  5. package/dist/__tests__/e2e/test-config.js.map +1 -0
  6. package/dist/adapter.d.ts +44 -0
  7. package/dist/adapter.d.ts.map +1 -1
  8. package/dist/adapter.js +655 -87
  9. package/dist/adapter.js.map +1 -1
  10. package/dist/agent.d.ts +8 -1
  11. package/dist/agent.d.ts.map +1 -1
  12. package/dist/agent.js +114 -5
  13. package/dist/agent.js.map +1 -1
  14. package/dist/app.d.ts.map +1 -1
  15. package/dist/app.js +19 -3
  16. package/dist/app.js.map +1 -1
  17. package/dist/config.d.ts.map +1 -1
  18. package/dist/config.js +33 -4
  19. package/dist/config.js.map +1 -1
  20. package/dist/helpers/env-mapper.d.ts +60 -1
  21. package/dist/helpers/env-mapper.d.ts.map +1 -1
  22. package/dist/helpers/env-mapper.js +136 -6
  23. package/dist/helpers/env-mapper.js.map +1 -1
  24. package/dist/index.d.ts +1 -1
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +6 -2
  27. package/dist/index.js.map +1 -1
  28. package/dist/runtime/audit-logger.d.ts +96 -0
  29. package/dist/runtime/audit-logger.d.ts.map +1 -0
  30. package/dist/runtime/audit-logger.js +276 -0
  31. package/dist/runtime/audit-logger.js.map +1 -0
  32. package/dist/runtime/oauth-handler.d.ts +5 -0
  33. package/dist/runtime/oauth-handler.d.ts.map +1 -1
  34. package/dist/runtime/oauth-handler.js +152 -35
  35. package/dist/runtime/oauth-handler.js.map +1 -1
  36. package/dist/runtime.d.ts +12 -1
  37. package/dist/runtime.d.ts.map +1 -1
  38. package/dist/runtime.js +34 -4
  39. package/dist/runtime.js.map +1 -1
  40. package/dist/server.d.ts.map +1 -1
  41. package/dist/server.js +7 -1
  42. package/dist/server.js.map +1 -1
  43. package/dist/services/admin.service.d.ts.map +1 -1
  44. package/dist/services/admin.service.js +15 -1
  45. package/dist/services/admin.service.js.map +1 -1
  46. package/dist/services/consent-audit.service.d.ts +91 -0
  47. package/dist/services/consent-audit.service.d.ts.map +1 -0
  48. package/dist/services/consent-audit.service.js +243 -0
  49. package/dist/services/consent-audit.service.js.map +1 -0
  50. package/dist/services/consent-config.service.d.ts +2 -2
  51. package/dist/services/consent-config.service.d.ts.map +1 -1
  52. package/dist/services/consent-config.service.js +55 -24
  53. package/dist/services/consent-config.service.js.map +1 -1
  54. package/dist/services/consent.service.d.ts +49 -1
  55. package/dist/services/consent.service.d.ts.map +1 -1
  56. package/dist/services/consent.service.js +1491 -28
  57. package/dist/services/consent.service.js.map +1 -1
  58. package/dist/services/delegation.service.d.ts.map +1 -1
  59. package/dist/services/delegation.service.js +67 -29
  60. package/dist/services/delegation.service.js.map +1 -1
  61. package/dist/services/proof.service.d.ts +5 -3
  62. package/dist/services/proof.service.d.ts.map +1 -1
  63. package/dist/services/proof.service.js +35 -8
  64. package/dist/services/proof.service.js.map +1 -1
  65. package/dist/types.d.ts +30 -0
  66. package/dist/types.d.ts.map +1 -1
  67. package/package.json +13 -9
@@ -0,0 +1,276 @@
1
+ /**
2
+ * Cloudflare Audit Logger
3
+ *
4
+ * Cloudflare Workers-compatible implementation of IAuditLogger using Web Crypto API.
5
+ * This implementation uses Web Crypto API instead of Node.js crypto for compatibility
6
+ * with Cloudflare Workers environment.
7
+ */
8
+ /**
9
+ * Format milliseconds into human-readable interval string
10
+ */
11
+ function formatTimeInterval(ms) {
12
+ if (ms === undefined || ms === null)
13
+ return "unknown";
14
+ if (ms === 0)
15
+ return "0ms";
16
+ const TIME_INTERVALS = {
17
+ SECOND: 1000,
18
+ MINUTE: 60 * 1000,
19
+ HOUR: 60 * 60 * 1000,
20
+ DAY: 24 * 60 * 60 * 1000,
21
+ WEEK: 7 * 24 * 60 * 60 * 1000,
22
+ };
23
+ if (ms % TIME_INTERVALS.WEEK === 0) {
24
+ const weeks = ms / TIME_INTERVALS.WEEK;
25
+ return weeks === 1 ? "weekly" : `${weeks}-weekly`;
26
+ }
27
+ if (ms % TIME_INTERVALS.DAY === 0) {
28
+ const days = ms / TIME_INTERVALS.DAY;
29
+ return days === 1 ? "daily" : `${days}-daily`;
30
+ }
31
+ if (ms % TIME_INTERVALS.HOUR === 0) {
32
+ const hours = ms / TIME_INTERVALS.HOUR;
33
+ return hours === 1 ? "hourly" : `${hours}-hourly`;
34
+ }
35
+ if (ms % TIME_INTERVALS.MINUTE === 0) {
36
+ const minutes = ms / TIME_INTERVALS.MINUTE;
37
+ return minutes === 1 ? "minutely" : `${minutes}-minutely`;
38
+ }
39
+ if (ms % TIME_INTERVALS.SECOND === 0) {
40
+ const seconds = ms / TIME_INTERVALS.SECOND;
41
+ return seconds === 1 ? "every-second" : `${seconds}-secondly`;
42
+ }
43
+ return `${ms}ms`;
44
+ }
45
+ /**
46
+ * Cloudflare-compatible audit logger implementation
47
+ *
48
+ * Uses Web Crypto API for cryptographic operations instead of Node.js crypto.
49
+ * Implements the same audit.v1 format and rotation logic as the Node.js version.
50
+ */
51
+ export class CloudflareAuditLogger {
52
+ config;
53
+ sessionAuditLog = new Set(); // Track first call per session
54
+ totalRecordsLogged = 0; // Total records logged (for count rotation)
55
+ currentLogSize = 0; // Current log size in bytes (for size rotation)
56
+ lastRotationTime = Date.now(); // Last rotation timestamp (for time rotation)
57
+ destroyed = false; // Track if logger has been destroyed
58
+ constructor(config = {}) {
59
+ const rotationConfig = config.rotation
60
+ ? {
61
+ strategy: "custom",
62
+ ...config.rotation,
63
+ }
64
+ : undefined;
65
+ this.config = {
66
+ enabled: true,
67
+ logFunction: console.log,
68
+ includePayloads: false,
69
+ ...config,
70
+ rotation: rotationConfig,
71
+ };
72
+ }
73
+ /**
74
+ * Log an audit record (with session deduplication)
75
+ */
76
+ async logAuditRecord(context) {
77
+ if (this.destroyed) {
78
+ throw new Error("CloudflareAuditLogger has been destroyed");
79
+ }
80
+ if (!this.config.enabled) {
81
+ return;
82
+ }
83
+ // Check if this is the first call for this session
84
+ const sessionKey = `${context.session.sessionId}:${context.session.audience}`;
85
+ if (this.sessionAuditLog.has(sessionKey)) {
86
+ return; // Already logged for this session
87
+ }
88
+ // Mark session as logged
89
+ this.sessionAuditLog.add(sessionKey);
90
+ // Create audit record
91
+ // Extract kid from identity (may be kid, keyId, or derived from did)
92
+ const kid = context.identity.kid ||
93
+ context.identity.keyId ||
94
+ context.identity.did.split(":").pop() ||
95
+ "unknown";
96
+ const auditRecord = {
97
+ version: "audit.v1",
98
+ ts: Math.floor(Date.now() / 1000),
99
+ session: context.session.sessionId,
100
+ audience: context.session.audience,
101
+ did: context.identity.did,
102
+ kid,
103
+ reqHash: context.requestHash,
104
+ resHash: context.responseHash,
105
+ verified: context.verified,
106
+ scope: context.scopeId || "-",
107
+ };
108
+ // Format as frozen audit line
109
+ const auditLine = this.formatAuditLine(auditRecord);
110
+ // Track size in bytes (UTF-8) - using TextEncoder instead of Buffer
111
+ const encoder = new TextEncoder();
112
+ const sizeBytes = encoder.encode(auditLine).length;
113
+ this.currentLogSize += sizeBytes;
114
+ this.totalRecordsLogged++;
115
+ // Emit audit record
116
+ this.config.logFunction(auditLine);
117
+ // Check if rotation is needed (event-driven)
118
+ await this.checkRotation();
119
+ }
120
+ /**
121
+ * Log an event (without session deduplication)
122
+ */
123
+ async logEvent(context) {
124
+ if (this.destroyed) {
125
+ throw new Error("CloudflareAuditLogger has been destroyed");
126
+ }
127
+ if (!this.config.enabled) {
128
+ return;
129
+ }
130
+ // Generate event hash using Web Crypto API
131
+ const eventHash = await this.hashEvent(context.eventType, context.eventData);
132
+ // Create audit record (same format as regular audit logs)
133
+ // Extract kid from identity (may be kid, keyId, or derived from did)
134
+ const kid = context.identity.kid ||
135
+ context.identity.keyId ||
136
+ context.identity.did.split(":").pop() ||
137
+ "unknown";
138
+ const auditRecord = {
139
+ version: "audit.v1",
140
+ ts: Math.floor(Date.now() / 1000),
141
+ session: context.session.sessionId,
142
+ audience: context.session.audience,
143
+ did: context.identity.did,
144
+ kid,
145
+ reqHash: `sha256:${eventHash}`,
146
+ resHash: `sha256:${eventHash}`, // Same hash for events
147
+ verified: "yes",
148
+ scope: context.eventType, // Use eventType as scope
149
+ };
150
+ // Format and log (NO session deduplication check)
151
+ const auditLine = this.formatAuditLine(auditRecord);
152
+ // Track size and count
153
+ const encoder = new TextEncoder();
154
+ const sizeBytes = encoder.encode(auditLine).length;
155
+ this.currentLogSize += sizeBytes;
156
+ this.totalRecordsLogged++;
157
+ // Emit audit record
158
+ this.config.logFunction(auditLine);
159
+ // Check rotation
160
+ await this.checkRotation();
161
+ }
162
+ /**
163
+ * Generate deterministic hash for event using Web Crypto API
164
+ */
165
+ async hashEvent(type, data) {
166
+ const content = JSON.stringify({
167
+ type,
168
+ data,
169
+ ts: Date.now(),
170
+ nonce: this.generateRandomHex(16),
171
+ });
172
+ // Use Web Crypto API for SHA-256 hashing
173
+ const encoder = new TextEncoder();
174
+ const dataBuffer = encoder.encode(content);
175
+ const hashBuffer = await crypto.subtle.digest("SHA-256", dataBuffer);
176
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
177
+ const hashHex = hashArray
178
+ .map((b) => b.toString(16).padStart(2, "0"))
179
+ .join("");
180
+ return hashHex;
181
+ }
182
+ /**
183
+ * Generate random hex string using Web Crypto API
184
+ */
185
+ generateRandomHex(length) {
186
+ const randomBytes = crypto.getRandomValues(new Uint8Array(length));
187
+ return Array.from(randomBytes)
188
+ .map((b) => b.toString(16).padStart(2, "0"))
189
+ .join("");
190
+ }
191
+ /**
192
+ * Format audit record as frozen audit line
193
+ * Format: audit.v1 ts=<unix> session=<id> audience=<host> did=<did> kid=<kid> reqHash=<sha256:..> resHash=<sha256:..> verified=yes|no scope=<scopeId|->
194
+ */
195
+ formatAuditLine(record) {
196
+ const fields = [
197
+ `${record.version}`,
198
+ `ts=${record.ts}`,
199
+ `session=${record.session}`,
200
+ `audience=${record.audience}`,
201
+ `did=${record.did}`,
202
+ `kid=${record.kid}`,
203
+ `reqHash=${record.reqHash}`,
204
+ `resHash=${record.resHash}`,
205
+ `verified=${record.verified}`,
206
+ `scope=${record.scope}`,
207
+ ];
208
+ return fields.join(" ");
209
+ }
210
+ /**
211
+ * Check if rotation is needed and trigger if necessary (event-driven)
212
+ */
213
+ async checkRotation() {
214
+ if (!this.config.rotation) {
215
+ return;
216
+ }
217
+ const { strategy, sizeLimit, timeInterval, countThreshold, hooks } = this.config.rotation;
218
+ let shouldRotate = false;
219
+ let trigger = "";
220
+ // Size-based rotation
221
+ if (strategy === "size" && sizeLimit && this.currentLogSize >= sizeLimit) {
222
+ shouldRotate = true;
223
+ trigger = "size-limit";
224
+ await hooks?.onSizeLimit?.(this.currentLogSize, sizeLimit);
225
+ }
226
+ // Time-based rotation (event-driven, not timer-based)
227
+ if (strategy === "time" &&
228
+ timeInterval &&
229
+ Date.now() - this.lastRotationTime >= timeInterval) {
230
+ shouldRotate = true;
231
+ trigger = "time-interval";
232
+ const interval = formatTimeInterval(timeInterval);
233
+ await hooks?.onTimeBased?.(interval);
234
+ }
235
+ // Count-based rotation
236
+ if (strategy === "count" &&
237
+ countThreshold &&
238
+ this.totalRecordsLogged >= countThreshold) {
239
+ shouldRotate = true;
240
+ trigger = "count-threshold";
241
+ await hooks?.onCountThreshold?.(this.totalRecordsLogged, countThreshold);
242
+ }
243
+ // Trigger rotation if needed
244
+ if (shouldRotate) {
245
+ await this.rotateNow(trigger);
246
+ }
247
+ }
248
+ /**
249
+ * Rotate audit log now (manually triggered)
250
+ */
251
+ async rotateNow(trigger = "manual") {
252
+ if (!this.config.rotation?.hooks?.onRotation) {
253
+ return;
254
+ }
255
+ const context = {
256
+ strategy: this.config.rotation.strategy || "custom",
257
+ trigger,
258
+ recordsLogged: this.totalRecordsLogged,
259
+ timestamp: Date.now(),
260
+ };
261
+ // Call rotation hook
262
+ await this.config.rotation.hooks.onRotation(context);
263
+ // Reset rotation counters
264
+ this.currentLogSize = 0;
265
+ this.lastRotationTime = Date.now();
266
+ this.totalRecordsLogged = 0;
267
+ }
268
+ /**
269
+ * Destroy the logger (cleanup)
270
+ */
271
+ destroy() {
272
+ this.destroyed = true;
273
+ this.sessionAuditLog.clear();
274
+ }
275
+ }
276
+ //# sourceMappingURL=audit-logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-logger.js","sourceRoot":"","sources":["../../src/runtime/audit-logger.ts"],"names":[],"mappings":"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"}
@@ -14,6 +14,7 @@ export interface HonoContext {
14
14
  header: (name: string, value: string) => void;
15
15
  }
16
16
  import type { ConsentService } from '../services/consent.service';
17
+ import type { OAuthSecurityService } from '../services/oauth-security.service';
17
18
  export interface OAuthCallbackConfig {
18
19
  /**
19
20
  * AgentShield API URL (defaults to AGENTSHIELD_API_URL env var)
@@ -27,6 +28,10 @@ export interface OAuthCallbackConfig {
27
28
  * ConsentService instance for OAuth identity linking (Phase 4)
28
29
  */
29
30
  consentService?: ConsentService;
31
+ /**
32
+ * OAuthSecurityService instance for CSRF-protected state validation
33
+ */
34
+ oauthSecurityService?: OAuthSecurityService;
30
35
  /**
31
36
  * Custom success HTML template (optional)
32
37
  */
@@ -1 +1 @@
1
- {"version":3,"file":"oauth-handler.d.ts","sourceRoot":"","sources":["../../src/runtime/oauth-handler.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,GAAG,CAAC;IACT,GAAG,EAAE;QACH,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;QAC3C,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;KAC9C,CAAC;IACF,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,QAAQ,CAAC;IAClD,IAAI,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,QAAQ,CAAC;IACjD,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/C;AAID,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAGlE,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;OAEG;IACH,iBAAiB,CAAC,EAAE,WAAW,CAAC;IAEhC;;OAEG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC;IAEhC;;OAEG;IACH,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,MAAM,CAAC;IAErD;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,MAAM,CAAC;IAElD;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,qBAAqB;IACpC,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA8KD;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,GAAE,mBAAwB,IAC3D,GAAG,WAAW,uBA4Q7B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAoBpE"}
1
+ {"version":3,"file":"oauth-handler.d.ts","sourceRoot":"","sources":["../../src/runtime/oauth-handler.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,GAAG,CAAC;IACT,GAAG,EAAE;QACH,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;QAC3C,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;KAC9C,CAAC;IACF,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,QAAQ,CAAC;IAClD,IAAI,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,QAAQ,CAAC;IACjD,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/C;AAID,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAE/E,MAAM,WAAW,mBAAmB;IAClC;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B;;OAEG;IACH,iBAAiB,CAAC,EAAE,WAAW,CAAC;IAEhC;;OAEG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC;IAEhC;;OAEG;IACH,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAE5C;;OAEG;IACH,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,MAAM,CAAC;IAErD;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,MAAM,CAAC;IAElD;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,qBAAqB;IACpC,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA8KD;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,GAAE,mBAAwB,IAC3D,GAAG,WAAW,uBAoY7B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAoBpE"}
@@ -184,7 +184,7 @@ export function createOAuthCallbackHandler(config = {}) {
184
184
  return async (c) => {
185
185
  const env = c.env;
186
186
  // Get configuration with defaults
187
- const { agentShieldApiUrl = env.AGENTSHIELD_API_URL || 'https://hobbs.work', delegationStorage, consentService, successTemplate = defaultSuccessTemplate, errorTemplate = defaultErrorTemplate, autoClose = true, autoCloseDelay = 5000 } = config;
187
+ const { agentShieldApiUrl = env.AGENTSHIELD_API_URL || 'https://hobbs.work', delegationStorage, consentService, oauthSecurityService, successTemplate = defaultSuccessTemplate, errorTemplate = defaultErrorTemplate, autoClose = true, autoCloseDelay = 5000 } = config;
188
188
  // Get query parameters
189
189
  const code = c.req.query('code');
190
190
  const stateParam = c.req.query('state');
@@ -192,7 +192,12 @@ export function createOAuthCallbackHandler(config = {}) {
192
192
  // Handle OAuth errors
193
193
  if (error) {
194
194
  const errorDescription = c.req.query('error_description') || 'Authorization failed';
195
- console.error('[OAuth] Error from provider:', error, errorDescription);
195
+ console.error('[OAuth] 🔒 SECURITY EVENT: Error from provider:', {
196
+ error,
197
+ errorDescription,
198
+ timestamp: new Date().toISOString(),
199
+ eventType: 'oauth_provider_error'
200
+ });
196
201
  const html = errorTemplate({
197
202
  error,
198
203
  description: errorDescription
@@ -201,25 +206,89 @@ export function createOAuthCallbackHandler(config = {}) {
201
206
  }
202
207
  // Validate required parameters
203
208
  if (!code || !stateParam) {
204
- console.error('[OAuth] Missing code or state parameter');
209
+ console.error('[OAuth] 🔒 SECURITY EVENT: Missing required parameters:', {
210
+ hasCode: !!code,
211
+ hasState: !!stateParam,
212
+ timestamp: new Date().toISOString(),
213
+ eventType: 'oauth_validation_failed',
214
+ reason: 'missing_parameters'
215
+ });
205
216
  const html = errorTemplate({
206
217
  error: 'invalid_request',
207
218
  description: 'Missing authorization code or state parameter'
208
219
  });
209
220
  return c.html(html, 400);
210
221
  }
211
- // Decode state parameter
222
+ // CSRF Protection: Retrieve and validate state from secure storage
212
223
  let state;
213
- try {
214
- state = JSON.parse(atob(stateParam));
224
+ let stateData = null;
225
+ if (oauthSecurityService) {
226
+ // Use secure state validation (CSRF-protected)
227
+ try {
228
+ stateData = await oauthSecurityService.getOAuthState(stateParam);
229
+ if (!stateData) {
230
+ console.error('[OAuth] 🔒 SECURITY EVENT: State validation failed - state not found or expired:', {
231
+ stateParam: stateParam.substring(0, 20) + '...',
232
+ timestamp: new Date().toISOString(),
233
+ eventType: 'csrf_protection_failed',
234
+ reason: 'state_not_found_or_expired'
235
+ });
236
+ const html = errorTemplate({
237
+ error: 'invalid_state',
238
+ description: 'Invalid or expired state parameter. This may be a CSRF attack or the authorization request has expired.'
239
+ });
240
+ return c.html(html, 400);
241
+ }
242
+ // Extract state from stored data
243
+ state = {
244
+ project_id: stateData.project_id,
245
+ agent_did: stateData.agent_did,
246
+ session_id: stateData.session_id,
247
+ delegation_id: stateData.delegation_id,
248
+ };
249
+ console.log('[OAuth] 🔒 SECURITY EVENT: State validated successfully:', {
250
+ projectId: state.project_id,
251
+ agentDid: state.agent_did.substring(0, 20) + '...',
252
+ sessionId: state.session_id?.substring(0, 20) + '...',
253
+ timestamp: new Date().toISOString(),
254
+ eventType: 'csrf_protection_success',
255
+ stateStoredAt: stateData.storedAt
256
+ });
257
+ }
258
+ catch (err) {
259
+ console.error('[OAuth] 🔒 SECURITY EVENT: State validation error:', {
260
+ error: err instanceof Error ? err.message : String(err),
261
+ stateParam: stateParam.substring(0, 20) + '...',
262
+ timestamp: new Date().toISOString(),
263
+ eventType: 'csrf_protection_error',
264
+ reason: 'validation_exception'
265
+ });
266
+ const html = errorTemplate({
267
+ error: 'invalid_state',
268
+ description: 'Failed to validate state parameter'
269
+ });
270
+ return c.html(html, 400);
271
+ }
215
272
  }
216
- catch (err) {
217
- console.error('[OAuth] Failed to decode state:', err);
218
- const html = errorTemplate({
219
- error: 'invalid_state',
220
- description: 'Invalid state parameter'
221
- });
222
- return c.html(html, 400);
273
+ else {
274
+ // Fallback: Decode state parameter directly (less secure, but backward compatible)
275
+ console.warn('[OAuth] ⚠️ SECURITY WARNING: OAuthSecurityService not provided, using insecure state decoding');
276
+ try {
277
+ state = JSON.parse(atob(stateParam));
278
+ }
279
+ catch (err) {
280
+ console.error('[OAuth] 🔒 SECURITY EVENT: Failed to decode state:', {
281
+ error: err instanceof Error ? err.message : String(err),
282
+ timestamp: new Date().toISOString(),
283
+ eventType: 'oauth_validation_failed',
284
+ reason: 'state_decode_error'
285
+ });
286
+ const html = errorTemplate({
287
+ error: 'invalid_state',
288
+ description: 'Invalid state parameter'
289
+ });
290
+ return c.html(html, 400);
291
+ }
223
292
  }
224
293
  const { project_id, agent_did, session_id, delegation_id } = state;
225
294
  // Validate session ID
@@ -231,11 +300,14 @@ export function createOAuthCallbackHandler(config = {}) {
231
300
  });
232
301
  return c.html(html, 400);
233
302
  }
234
- console.log('[OAuth] Processing authorization code exchange:', {
303
+ console.log('[OAuth] 🔒 SECURITY EVENT: Processing authorization code exchange:', {
235
304
  projectId: project_id,
236
305
  agentDid: agent_did.substring(0, 20) + '...',
237
- sessionId: session_id,
238
- delegationId: delegation_id
306
+ sessionId: session_id?.substring(0, 20) + '...',
307
+ delegationId: delegation_id,
308
+ timestamp: new Date().toISOString(),
309
+ eventType: 'oauth_code_exchange_start',
310
+ hasSecureState: !!oauthSecurityService
239
311
  });
240
312
  try {
241
313
  // Exchange authorization code for delegation token
@@ -255,9 +327,13 @@ export function createOAuthCallbackHandler(config = {}) {
255
327
  });
256
328
  if (!tokenResponse.ok) {
257
329
  const errorText = await tokenResponse.text();
258
- console.error('[OAuth] Token exchange failed:', {
330
+ console.error('[OAuth] 🔒 SECURITY EVENT: Token exchange failed:', {
259
331
  status: tokenResponse.status,
260
- error: errorText
332
+ error: errorText.substring(0, 200),
333
+ projectId: project_id,
334
+ agentDid: agent_did.substring(0, 20) + '...',
335
+ timestamp: new Date().toISOString(),
336
+ eventType: 'oauth_token_exchange_failed'
261
337
  });
262
338
  const html = errorTemplate({
263
339
  error: 'token_exchange_failed',
@@ -268,18 +344,26 @@ export function createOAuthCallbackHandler(config = {}) {
268
344
  const tokenData = await tokenResponse.json();
269
345
  // Validate token response
270
346
  if (!tokenData.delegation_token) {
271
- console.error('[OAuth] No delegation token in response:', tokenData);
347
+ console.error('[OAuth] 🔒 SECURITY EVENT: Invalid token response:', {
348
+ hasDelegationToken: !!tokenData.delegation_token,
349
+ responseKeys: Object.keys(tokenData),
350
+ projectId: project_id,
351
+ timestamp: new Date().toISOString(),
352
+ eventType: 'oauth_invalid_token_response'
353
+ });
272
354
  const html = errorTemplate({
273
355
  error: 'invalid_response',
274
356
  description: 'Invalid token response from authorization server'
275
357
  });
276
358
  return c.html(html, 500);
277
359
  }
278
- console.log('[OAuth] Token exchange successful:', {
360
+ console.log('[OAuth] 🔒 SECURITY EVENT: Token exchange successful:', {
279
361
  delegationId: tokenData.delegation_id,
280
362
  sessionId: tokenData.session_id || session_id,
281
363
  expiresIn: tokenData.expires_in,
282
- scopes: tokenData.scopes
364
+ scopes: tokenData.scopes,
365
+ timestamp: new Date().toISOString(),
366
+ eventType: 'oauth_token_exchange_success'
283
367
  });
284
368
  // Phase 4 PR #3: Extract OAuth user info and link to User DID
285
369
  let oauthIdentity = null;
@@ -320,16 +404,26 @@ export function createOAuthCallbackHandler(config = {}) {
320
404
  // Set OAuth identity cookie for consent page
321
405
  const cookieValue = encodeURIComponent(JSON.stringify(oauthIdentity));
322
406
  c.header("Set-Cookie", `oauth_identity=${cookieValue}; HttpOnly; Secure; SameSite=Lax; Max-Age=604800; Path=/`);
323
- console.log('[OAuth] OAuth identity linked and cookie set:', {
407
+ console.log('[OAuth] 🔒 SECURITY EVENT: OAuth identity linked and cookie set:', {
324
408
  provider: oauthIdentity.provider,
325
409
  subject: oauthIdentity.subject.substring(0, 20) + '...',
326
410
  userDid: userDid.substring(0, 20) + '...',
411
+ sessionId: session_id?.substring(0, 20) + '...',
412
+ timestamp: new Date().toISOString(),
413
+ eventType: 'oauth_identity_linked',
414
+ cookieSet: true
327
415
  });
328
416
  }
329
417
  }
330
418
  catch (error) {
331
419
  // OAuth linking errors are non-fatal - log but continue
332
- console.error('[OAuth] Failed to link OAuth identity (non-fatal):', error);
420
+ console.error('[OAuth] 🔒 SECURITY EVENT: Failed to link OAuth identity (non-fatal):', {
421
+ error: error instanceof Error ? error.message : String(error),
422
+ sessionId: session_id?.substring(0, 20) + '...',
423
+ timestamp: new Date().toISOString(),
424
+ eventType: 'oauth_identity_linking_failed',
425
+ severity: 'warning'
426
+ });
333
427
  }
334
428
  }
335
429
  // Store delegation token in KV if storage is configured
@@ -350,11 +444,15 @@ export function createOAuthCallbackHandler(config = {}) {
350
444
  await delegationStorage.put(userAgentKey, tokenData.delegation_token, {
351
445
  expirationTtl: ttl
352
446
  });
353
- console.log('[OAuth] Delegation token stored with user+agent DID:', {
354
- key: userAgentKey,
447
+ console.log('[OAuth] 🔒 SECURITY EVENT: Delegation token stored with user+agent DID:', {
448
+ key: userAgentKey.substring(0, 50) + '...',
355
449
  ttl,
356
450
  agentDid: agent_did.substring(0, 20) + '...',
357
- delegationId: tokenData.delegation_id
451
+ userDid: sessionUserDid.substring(0, 20) + '...',
452
+ delegationId: tokenData.delegation_id,
453
+ timestamp: new Date().toISOString(),
454
+ eventType: 'delegation_token_stored',
455
+ storageType: 'user_agent_scoped'
358
456
  });
359
457
  }
360
458
  // Backward compatibility: Agent-only key (24 hour TTL)
@@ -362,11 +460,15 @@ export function createOAuthCallbackHandler(config = {}) {
362
460
  await delegationStorage.put(legacyKey, tokenData.delegation_token, {
363
461
  expirationTtl: 24 * 60 * 60 // 24 hours only
364
462
  });
365
- console.log('[OAuth] Delegation token stored with legacy agent key:', {
366
- key: legacyKey,
463
+ console.log('[OAuth] 🔒 SECURITY EVENT: Delegation token stored with legacy agent key:', {
464
+ key: legacyKey.substring(0, 50) + '...',
367
465
  ttl: 24 * 60 * 60,
368
466
  agentDid: agent_did.substring(0, 20) + '...',
369
- delegationId: tokenData.delegation_id
467
+ delegationId: tokenData.delegation_id,
468
+ timestamp: new Date().toISOString(),
469
+ eventType: 'delegation_token_stored',
470
+ storageType: 'legacy_agent_scoped',
471
+ warning: 'Legacy format - migrate to user+agent scoped tokens'
370
472
  });
371
473
  // Session cache for fast lookup (shorter TTL for performance)
372
474
  await delegationStorage.put(sessionKey, JSON.stringify({
@@ -377,16 +479,25 @@ export function createOAuthCallbackHandler(config = {}) {
377
479
  }), {
378
480
  expirationTtl: Math.min(ttl, 1800) // 30 minutes or token TTL, whichever is shorter
379
481
  });
380
- console.log('[OAuth] Delegation token cached for session:', {
381
- key: sessionKey,
482
+ console.log('[OAuth] 🔒 SECURITY EVENT: Delegation token cached for session:', {
483
+ key: sessionKey.substring(0, 50) + '...',
382
484
  ttl: Math.min(ttl, 1800),
383
- sessionId: session_id,
384
- userDid: sessionUserDid,
485
+ sessionId: session_id?.substring(0, 20) + '...',
486
+ userDid: sessionUserDid?.substring(0, 20) + '...',
487
+ timestamp: new Date().toISOString(),
488
+ eventType: 'delegation_token_cached',
489
+ storageType: 'session_cache'
385
490
  });
386
491
  }
387
492
  catch (storageError) {
388
493
  // Storage errors are non-fatal - log but continue
389
- console.error('[OAuth] Storage error (non-fatal):', storageError);
494
+ console.error('[OAuth] 🔒 SECURITY EVENT: Storage error (non-fatal):', {
495
+ error: storageError instanceof Error ? storageError.message : String(storageError),
496
+ sessionId: session_id?.substring(0, 20) + '...',
497
+ timestamp: new Date().toISOString(),
498
+ eventType: 'delegation_storage_error',
499
+ severity: 'warning'
500
+ });
390
501
  }
391
502
  }
392
503
  // Return success page
@@ -399,7 +510,13 @@ export function createOAuthCallbackHandler(config = {}) {
399
510
  return c.html(html);
400
511
  }
401
512
  catch (error) {
402
- console.error('[OAuth] Unexpected error:', error);
513
+ console.error('[OAuth] 🔒 SECURITY EVENT: Unexpected error:', {
514
+ error: error instanceof Error ? error.message : String(error),
515
+ stack: error instanceof Error ? error.stack : undefined,
516
+ timestamp: new Date().toISOString(),
517
+ eventType: 'oauth_unexpected_error',
518
+ severity: 'error'
519
+ });
403
520
  const html = errorTemplate({
404
521
  error: 'internal_error',
405
522
  description: error instanceof Error ? error.message : 'An unexpected error occurred'