@kya-os/mcp-i-core 1.2.3-canary.6 → 1.3.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 (231) hide show
  1. package/.claude/settings.local.json +9 -0
  2. package/.turbo/turbo-build.log +4 -0
  3. package/.turbo/turbo-test$colon$coverage.log +4514 -0
  4. package/.turbo/turbo-test.log +2973 -0
  5. package/COMPLIANCE_IMPROVEMENT_REPORT.md +483 -0
  6. package/Composer 3.md +615 -0
  7. package/GPT-5.md +1169 -0
  8. package/OPUS-plan.md +352 -0
  9. package/PHASE_3_AND_4.1_SUMMARY.md +585 -0
  10. package/PHASE_3_SUMMARY.md +317 -0
  11. package/PHASE_4.1.3_SUMMARY.md +428 -0
  12. package/PHASE_4.1_COMPLETE.md +525 -0
  13. package/PHASE_4_USER_DID_IDENTITY_LINKING_PLAN.md +1240 -0
  14. package/SCHEMA_COMPLIANCE_REPORT.md +275 -0
  15. package/TEST_PLAN.md +571 -0
  16. package/coverage/coverage-final.json +57 -0
  17. package/dist/__tests__/utils/mock-providers.d.ts +1 -2
  18. package/dist/__tests__/utils/mock-providers.d.ts.map +1 -1
  19. package/dist/__tests__/utils/mock-providers.js.map +1 -1
  20. package/dist/cache/oauth-config-cache.d.ts +69 -0
  21. package/dist/cache/oauth-config-cache.d.ts.map +1 -0
  22. package/dist/cache/oauth-config-cache.js +76 -0
  23. package/dist/cache/oauth-config-cache.js.map +1 -0
  24. package/dist/identity/idp-token-resolver.d.ts +53 -0
  25. package/dist/identity/idp-token-resolver.d.ts.map +1 -0
  26. package/dist/identity/idp-token-resolver.js +108 -0
  27. package/dist/identity/idp-token-resolver.js.map +1 -0
  28. package/dist/identity/idp-token-storage.interface.d.ts +42 -0
  29. package/dist/identity/idp-token-storage.interface.d.ts.map +1 -0
  30. package/dist/identity/idp-token-storage.interface.js +12 -0
  31. package/dist/identity/idp-token-storage.interface.js.map +1 -0
  32. package/dist/identity/user-did-manager.d.ts +39 -1
  33. package/dist/identity/user-did-manager.d.ts.map +1 -1
  34. package/dist/identity/user-did-manager.js +69 -3
  35. package/dist/identity/user-did-manager.js.map +1 -1
  36. package/dist/index.d.ts +22 -0
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +39 -1
  39. package/dist/index.js.map +1 -1
  40. package/dist/runtime/audit-logger.d.ts +37 -0
  41. package/dist/runtime/audit-logger.d.ts.map +1 -0
  42. package/dist/runtime/audit-logger.js +9 -0
  43. package/dist/runtime/audit-logger.js.map +1 -0
  44. package/dist/runtime/base.d.ts +58 -2
  45. package/dist/runtime/base.d.ts.map +1 -1
  46. package/dist/runtime/base.js +266 -11
  47. package/dist/runtime/base.js.map +1 -1
  48. package/dist/services/access-control.service.d.ts.map +1 -1
  49. package/dist/services/access-control.service.js +200 -35
  50. package/dist/services/access-control.service.js.map +1 -1
  51. package/dist/services/authorization/authorization-registry.d.ts +29 -0
  52. package/dist/services/authorization/authorization-registry.d.ts.map +1 -0
  53. package/dist/services/authorization/authorization-registry.js +57 -0
  54. package/dist/services/authorization/authorization-registry.js.map +1 -0
  55. package/dist/services/authorization/types.d.ts +53 -0
  56. package/dist/services/authorization/types.d.ts.map +1 -0
  57. package/dist/services/authorization/types.js +10 -0
  58. package/dist/services/authorization/types.js.map +1 -0
  59. package/dist/services/batch-delegation.service.d.ts +53 -0
  60. package/dist/services/batch-delegation.service.d.ts.map +1 -0
  61. package/dist/services/batch-delegation.service.js +95 -0
  62. package/dist/services/batch-delegation.service.js.map +1 -0
  63. package/dist/services/oauth-config.service.d.ts +53 -0
  64. package/dist/services/oauth-config.service.d.ts.map +1 -0
  65. package/dist/services/oauth-config.service.js +117 -0
  66. package/dist/services/oauth-config.service.js.map +1 -0
  67. package/dist/services/oauth-provider-registry.d.ts +77 -0
  68. package/dist/services/oauth-provider-registry.d.ts.map +1 -0
  69. package/dist/services/oauth-provider-registry.js +112 -0
  70. package/dist/services/oauth-provider-registry.js.map +1 -0
  71. package/dist/services/oauth-service.d.ts +77 -0
  72. package/dist/services/oauth-service.d.ts.map +1 -0
  73. package/dist/services/oauth-service.js +348 -0
  74. package/dist/services/oauth-service.js.map +1 -0
  75. package/dist/services/oauth-token-retrieval.service.d.ts +49 -0
  76. package/dist/services/oauth-token-retrieval.service.d.ts.map +1 -0
  77. package/dist/services/oauth-token-retrieval.service.js +150 -0
  78. package/dist/services/oauth-token-retrieval.service.js.map +1 -0
  79. package/dist/services/provider-resolver.d.ts +48 -0
  80. package/dist/services/provider-resolver.d.ts.map +1 -0
  81. package/dist/services/provider-resolver.js +120 -0
  82. package/dist/services/provider-resolver.js.map +1 -0
  83. package/dist/services/provider-validator.d.ts +55 -0
  84. package/dist/services/provider-validator.d.ts.map +1 -0
  85. package/dist/services/provider-validator.js +135 -0
  86. package/dist/services/provider-validator.js.map +1 -0
  87. package/dist/services/tool-context-builder.d.ts +57 -0
  88. package/dist/services/tool-context-builder.d.ts.map +1 -0
  89. package/dist/services/tool-context-builder.js +125 -0
  90. package/dist/services/tool-context-builder.js.map +1 -0
  91. package/dist/services/tool-protection.service.d.ts +87 -10
  92. package/dist/services/tool-protection.service.d.ts.map +1 -1
  93. package/dist/services/tool-protection.service.js +282 -112
  94. package/dist/services/tool-protection.service.js.map +1 -1
  95. package/dist/types/oauth-required-error.d.ts +40 -0
  96. package/dist/types/oauth-required-error.d.ts.map +1 -0
  97. package/dist/types/oauth-required-error.js +40 -0
  98. package/dist/types/oauth-required-error.js.map +1 -0
  99. package/dist/utils/did-helpers.d.ts +33 -0
  100. package/dist/utils/did-helpers.d.ts.map +1 -1
  101. package/dist/utils/did-helpers.js +40 -0
  102. package/dist/utils/did-helpers.js.map +1 -1
  103. package/dist/utils/index.d.ts +1 -0
  104. package/dist/utils/index.d.ts.map +1 -1
  105. package/dist/utils/index.js +1 -0
  106. package/dist/utils/index.js.map +1 -1
  107. package/docs/API_REFERENCE.md +1362 -0
  108. package/docs/COMPLIANCE_MATRIX.md +691 -0
  109. package/docs/STATUSLIST2021_GUIDE.md +696 -0
  110. package/docs/W3C_VC_DELEGATION_GUIDE.md +710 -0
  111. package/package.json +24 -50
  112. package/scripts/audit-compliance.ts +724 -0
  113. package/src/__tests__/cache/tool-protection-cache.test.ts +640 -0
  114. package/src/__tests__/config/provider-runtime-config.test.ts +309 -0
  115. package/src/__tests__/delegation-e2e.test.ts +690 -0
  116. package/src/__tests__/identity/user-did-manager.test.ts +213 -0
  117. package/src/__tests__/index.test.ts +56 -0
  118. package/src/__tests__/integration/full-flow.test.ts +776 -0
  119. package/src/__tests__/integration.test.ts +281 -0
  120. package/src/__tests__/providers/base.test.ts +173 -0
  121. package/src/__tests__/providers/memory.test.ts +319 -0
  122. package/src/__tests__/regression/phase2-regression.test.ts +427 -0
  123. package/src/__tests__/runtime/audit-logger.test.ts +154 -0
  124. package/src/__tests__/runtime/base-extensions.test.ts +593 -0
  125. package/src/__tests__/runtime/base.test.ts +869 -0
  126. package/src/__tests__/runtime/delegation-flow.test.ts +164 -0
  127. package/src/__tests__/runtime/proof-client-did.test.ts +375 -0
  128. package/src/__tests__/runtime/route-interception.test.ts +686 -0
  129. package/src/__tests__/runtime/tool-protection-enforcement.test.ts +908 -0
  130. package/src/__tests__/services/agentshield-integration.test.ts +784 -0
  131. package/src/__tests__/services/provider-resolver-edge-cases.test.ts +487 -0
  132. package/src/__tests__/services/tool-protection-oauth-provider.test.ts +480 -0
  133. package/src/__tests__/services/tool-protection.service.test.ts +1366 -0
  134. package/src/__tests__/utils/mock-providers.ts +340 -0
  135. package/src/cache/oauth-config-cache.d.ts +69 -0
  136. package/src/cache/oauth-config-cache.d.ts.map +1 -0
  137. package/src/cache/oauth-config-cache.js +71 -0
  138. package/src/cache/oauth-config-cache.js.map +1 -0
  139. package/src/cache/oauth-config-cache.ts +123 -0
  140. package/src/cache/tool-protection-cache.ts +171 -0
  141. package/src/compliance/EXAMPLE.md +412 -0
  142. package/src/compliance/__tests__/schema-verifier.test.ts +797 -0
  143. package/src/compliance/index.ts +8 -0
  144. package/src/compliance/schema-registry.ts +460 -0
  145. package/src/compliance/schema-verifier.ts +708 -0
  146. package/src/config/__tests__/remote-config.spec.ts +268 -0
  147. package/src/config/remote-config.ts +174 -0
  148. package/src/config.ts +309 -0
  149. package/src/delegation/__tests__/audience-validator.test.ts +112 -0
  150. package/src/delegation/__tests__/bitstring.test.ts +346 -0
  151. package/src/delegation/__tests__/cascading-revocation.test.ts +628 -0
  152. package/src/delegation/__tests__/delegation-graph.test.ts +584 -0
  153. package/src/delegation/__tests__/utils.test.ts +152 -0
  154. package/src/delegation/__tests__/vc-issuer.test.ts +442 -0
  155. package/src/delegation/__tests__/vc-verifier.test.ts +922 -0
  156. package/src/delegation/audience-validator.ts +52 -0
  157. package/src/delegation/bitstring.ts +278 -0
  158. package/src/delegation/cascading-revocation.ts +370 -0
  159. package/src/delegation/delegation-graph.ts +299 -0
  160. package/src/delegation/index.ts +14 -0
  161. package/src/delegation/statuslist-manager.ts +353 -0
  162. package/src/delegation/storage/__tests__/memory-graph-storage.test.ts +366 -0
  163. package/src/delegation/storage/__tests__/memory-statuslist-storage.test.ts +228 -0
  164. package/src/delegation/storage/index.ts +9 -0
  165. package/src/delegation/storage/memory-graph-storage.ts +178 -0
  166. package/src/delegation/storage/memory-statuslist-storage.ts +77 -0
  167. package/src/delegation/utils.ts +42 -0
  168. package/src/delegation/vc-issuer.ts +232 -0
  169. package/src/delegation/vc-verifier.ts +568 -0
  170. package/src/identity/idp-token-resolver.ts +147 -0
  171. package/src/identity/idp-token-storage.interface.ts +59 -0
  172. package/src/identity/user-did-manager.ts +370 -0
  173. package/src/index.ts +260 -0
  174. package/src/providers/base.d.ts +91 -0
  175. package/src/providers/base.d.ts.map +1 -0
  176. package/src/providers/base.js +38 -0
  177. package/src/providers/base.js.map +1 -0
  178. package/src/providers/base.ts +96 -0
  179. package/src/providers/memory.ts +142 -0
  180. package/src/runtime/audit-logger.ts +39 -0
  181. package/src/runtime/base.ts +1329 -0
  182. package/src/services/__tests__/access-control.integration.test.ts +443 -0
  183. package/src/services/__tests__/access-control.proof-response-validation.test.ts +578 -0
  184. package/src/services/__tests__/access-control.service.test.ts +970 -0
  185. package/src/services/__tests__/batch-delegation.service.test.ts +351 -0
  186. package/src/services/__tests__/crypto.service.test.ts +531 -0
  187. package/src/services/__tests__/oauth-provider-registry.test.ts +142 -0
  188. package/src/services/__tests__/proof-verifier.integration.test.ts +485 -0
  189. package/src/services/__tests__/proof-verifier.test.ts +489 -0
  190. package/src/services/__tests__/provider-resolution.integration.test.ts +198 -0
  191. package/src/services/__tests__/provider-resolver.test.ts +217 -0
  192. package/src/services/__tests__/storage.service.test.ts +358 -0
  193. package/src/services/access-control.service.ts +990 -0
  194. package/src/services/authorization/authorization-registry.ts +66 -0
  195. package/src/services/authorization/types.ts +71 -0
  196. package/src/services/batch-delegation.service.ts +137 -0
  197. package/src/services/crypto.service.ts +302 -0
  198. package/src/services/errors.ts +76 -0
  199. package/src/services/index.ts +9 -0
  200. package/src/services/oauth-config.service.d.ts +53 -0
  201. package/src/services/oauth-config.service.d.ts.map +1 -0
  202. package/src/services/oauth-config.service.js +113 -0
  203. package/src/services/oauth-config.service.js.map +1 -0
  204. package/src/services/oauth-config.service.ts +166 -0
  205. package/src/services/oauth-provider-registry.d.ts +57 -0
  206. package/src/services/oauth-provider-registry.d.ts.map +1 -0
  207. package/src/services/oauth-provider-registry.js +73 -0
  208. package/src/services/oauth-provider-registry.js.map +1 -0
  209. package/src/services/oauth-provider-registry.ts +123 -0
  210. package/src/services/oauth-service.ts +510 -0
  211. package/src/services/oauth-token-retrieval.service.ts +245 -0
  212. package/src/services/proof-verifier.ts +478 -0
  213. package/src/services/provider-resolver.d.ts +48 -0
  214. package/src/services/provider-resolver.d.ts.map +1 -0
  215. package/src/services/provider-resolver.js +106 -0
  216. package/src/services/provider-resolver.js.map +1 -0
  217. package/src/services/provider-resolver.ts +144 -0
  218. package/src/services/provider-validator.ts +170 -0
  219. package/src/services/storage.service.ts +566 -0
  220. package/src/services/tool-context-builder.ts +172 -0
  221. package/src/services/tool-protection.service.ts +958 -0
  222. package/src/types/oauth-required-error.ts +63 -0
  223. package/src/types/tool-protection.ts +155 -0
  224. package/src/utils/__tests__/did-helpers.test.ts +101 -0
  225. package/src/utils/base64.ts +148 -0
  226. package/src/utils/cors.ts +83 -0
  227. package/src/utils/did-helpers.ts +150 -0
  228. package/src/utils/index.ts +8 -0
  229. package/src/utils/storage-keys.ts +278 -0
  230. package/tsconfig.json +21 -0
  231. package/vitest.config.ts +56 -0
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Delegation Verification Flow Integration Tests
3
+ *
4
+ * Tests the complete flow: handshake → session creation → tool call → delegation verification
5
+ * Ensures that session.agentDid is correctly used in delegation verification.
6
+ *
7
+ * @package @kya-os/mcp-i-core/__tests__/runtime
8
+ */
9
+
10
+ import { describe, it, expect, beforeEach } from "vitest";
11
+ import { MCPIRuntimeBase } from "../../runtime/base";
12
+ import { ProviderRuntimeConfig } from "../../config";
13
+ import {
14
+ createMockProviders,
15
+ MockClockProvider,
16
+ MockIdentityProvider,
17
+ MockNonceCacheProvider,
18
+ } from "../utils/mock-providers";
19
+ import type { DelegationRecord } from "@kya-os/contracts/delegation";
20
+
21
+ describe("Delegation Verification Flow", () => {
22
+ let runtime: MCPIRuntimeBase;
23
+ let config: ProviderRuntimeConfig;
24
+ let mockProviders: ReturnType<typeof createMockProviders>;
25
+
26
+ beforeEach(async () => {
27
+ mockProviders = createMockProviders();
28
+ config = {
29
+ ...mockProviders,
30
+ environment: "development",
31
+ session: {
32
+ timestampSkewSeconds: 120,
33
+ ttlMinutes: 30,
34
+ },
35
+ audit: {
36
+ enabled: false, // Disable audit for cleaner test output
37
+ },
38
+ };
39
+ runtime = new MCPIRuntimeBase(config);
40
+ await runtime.initialize();
41
+ });
42
+
43
+ it("should verify delegation using session.agentDid", async () => {
44
+ const agentDid = "did:key:zagent123";
45
+ const userDid = "did:web:user.example.com";
46
+
47
+ // 1. Perform handshake with agentDid
48
+ const handshakeRequest = {
49
+ nonce: "test-nonce-123",
50
+ audience: "https://server.example.com",
51
+ timestamp: Math.floor(Date.now() / 1000),
52
+ agentDid,
53
+ clientDid: userDid,
54
+ };
55
+
56
+ const handshakeResponse = await runtime.handleHandshake(handshakeRequest);
57
+ expect(handshakeResponse.sessionId).toBeDefined();
58
+
59
+ // 2. Get session and verify agentDid is correct
60
+ const session = await runtime.getCurrentSession();
61
+ expect(session).toBeDefined();
62
+ expect(session?.agentDid).toBe(agentDid); // ✅ Should be client's agent DID, not server DID
63
+ expect(session?.serverDid).toBe("did:key:zmock123"); // ✅ Should be server's DID
64
+
65
+ // 3. Create a delegation with subjectDid matching agentDid
66
+ // (In real scenario, this would be verified via delegation verifier)
67
+ const delegation: DelegationRecord = {
68
+ id: "del_test_001",
69
+ issuerDid: userDid,
70
+ subjectDid: agentDid, // ✅ Matches session.agentDid
71
+ controller: "user_alice",
72
+ vcId: "vc_test_001",
73
+ constraints: {
74
+ scopes: ["tool:execute"],
75
+ },
76
+ createdAt: Date.now(),
77
+ expiresAt: Date.now() + 3600000, // 1 hour
78
+ };
79
+
80
+ // 4. Verify that delegation.subjectDid matches session.agentDid
81
+ expect(delegation.subjectDid).toBe(session?.agentDid);
82
+ expect(delegation.subjectDid).not.toBe(session?.serverDid); // ✅ Should NOT match server DID
83
+ });
84
+
85
+ it("should fail when session.agentDid does not match delegation.subjectDid", async () => {
86
+ const agentDid = "did:key:zagent123";
87
+ const wrongAgentDid = "did:key:zwrong456";
88
+ const userDid = "did:web:user.example.com";
89
+
90
+ // 1. Perform handshake with agentDid
91
+ const handshakeRequest = {
92
+ nonce: "test-nonce-456",
93
+ audience: "https://server.example.com",
94
+ timestamp: Math.floor(Date.now() / 1000),
95
+ agentDid,
96
+ clientDid: userDid,
97
+ };
98
+
99
+ await runtime.handleHandshake(handshakeRequest);
100
+
101
+ // 2. Get session
102
+ const session = await runtime.getCurrentSession();
103
+ expect(session?.agentDid).toBe(agentDid);
104
+
105
+ // 3. Create delegation with wrong subjectDid
106
+ const delegation: DelegationRecord = {
107
+ id: "del_test_002",
108
+ issuerDid: userDid,
109
+ subjectDid: wrongAgentDid, // ❌ Does NOT match session.agentDid
110
+ controller: "user_bob",
111
+ vcId: "vc_test_002",
112
+ constraints: {
113
+ scopes: ["tool:execute"],
114
+ },
115
+ createdAt: Date.now(),
116
+ expiresAt: Date.now() + 3600000,
117
+ };
118
+
119
+ // 4. Verify mismatch
120
+ expect(delegation.subjectDid).not.toBe(session?.agentDid);
121
+ expect(delegation.subjectDid).toBe(wrongAgentDid);
122
+ });
123
+
124
+ it("should handle session without agentDid gracefully", async () => {
125
+ const userDid = "did:web:user.example.com";
126
+
127
+ // 1. Perform handshake WITHOUT agentDid
128
+ const handshakeRequest = {
129
+ nonce: "test-nonce-789",
130
+ audience: "https://server.example.com",
131
+ timestamp: Math.floor(Date.now() / 1000),
132
+ // agentDid not provided
133
+ clientDid: userDid,
134
+ };
135
+
136
+ await runtime.handleHandshake(handshakeRequest);
137
+
138
+ // 2. Get session
139
+ const session = await runtime.getCurrentSession();
140
+ expect(session?.agentDid).toBeUndefined(); // ✅ Should be undefined, not fallback to clientDid
141
+ expect(session?.serverDid).toBe("did:key:zmock123"); // ✅ Server DID should still be set
142
+ });
143
+
144
+ it("should verify serverDid is set correctly in session", async () => {
145
+ const agentDid = "did:key:zagent789";
146
+ const identity = await runtime.getIdentity();
147
+
148
+ // 1. Perform handshake
149
+ const handshakeRequest = {
150
+ nonce: "test-nonce-server",
151
+ audience: "https://server.example.com",
152
+ timestamp: Math.floor(Date.now() / 1000),
153
+ agentDid,
154
+ };
155
+
156
+ await runtime.handleHandshake(handshakeRequest);
157
+
158
+ // 2. Verify serverDid matches identity.did
159
+ const session = await runtime.getCurrentSession();
160
+ expect(session?.serverDid).toBe(identity.did);
161
+ expect(session?.serverDid).not.toBe(agentDid); // ✅ Should NOT match agent DID
162
+ });
163
+ });
164
+
@@ -0,0 +1,375 @@
1
+ /**
2
+ * Proof Client DID Tests
3
+ *
4
+ * Tests for proof generation with clientDid tracking and extraction.
5
+ */
6
+
7
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
8
+ import { MCPIRuntimeBase } from '../../runtime/base';
9
+ import { ProviderRuntimeConfig } from '../../config';
10
+ import { createMockProviders, MockClockProvider } from '../utils/mock-providers';
11
+
12
+ describe('MCPIRuntimeBase - Proof Client DID', () => {
13
+ let runtime: MCPIRuntimeBase;
14
+ let config: ProviderRuntimeConfig;
15
+ let mockProviders: ReturnType<typeof createMockProviders>;
16
+
17
+ beforeEach(async () => {
18
+ vi.clearAllMocks();
19
+ mockProviders = createMockProviders();
20
+ config = {
21
+ ...mockProviders,
22
+ environment: 'development',
23
+ session: {
24
+ timestampSkewSeconds: 120,
25
+ ttlMinutes: 30
26
+ },
27
+ audit: {
28
+ enabled: false
29
+ }
30
+ };
31
+ runtime = new MCPIRuntimeBase(config);
32
+ await runtime.initialize();
33
+ });
34
+
35
+ describe('createProof with clientDid', () => {
36
+ it('should include clientDid in proof when provided in session', async () => {
37
+ const testData = { result: 'success' };
38
+ const session = {
39
+ id: 'session123',
40
+ audience: 'https://client.example.com',
41
+ nonce: 'test-nonce',
42
+ clientDid: 'did:key:zclient123'
43
+ };
44
+
45
+ const proof = await runtime.createProof(testData, session);
46
+
47
+ expect(proof).toBeDefined();
48
+ expect(proof.did).toBe('did:key:zmock123'); // Agent DID
49
+ expect(proof.sessionId).toBe('session123');
50
+ expect(proof.audience).toBe('https://client.example.com');
51
+ // Note: clientDid is stored in proofData but not directly in proof object
52
+ // The proof structure includes sessionId which can be used to retrieve clientDid
53
+ });
54
+
55
+ it('should include userDid in proof when provided in session', async () => {
56
+ const testData = { result: 'success' };
57
+ const session = {
58
+ id: 'session123',
59
+ audience: 'https://client.example.com',
60
+ nonce: 'test-nonce',
61
+ userDid: 'did:key:zuser456'
62
+ };
63
+
64
+ const proof = await runtime.createProof(testData, session);
65
+
66
+ expect(proof).toBeDefined();
67
+ expect(proof.sessionId).toBe('session123');
68
+ // userDid is stored in session context
69
+ });
70
+
71
+ it('should work without clientDid or userDid (backward compatibility)', async () => {
72
+ const testData = { result: 'success' };
73
+ const session = {
74
+ id: 'session123',
75
+ audience: 'https://client.example.com',
76
+ nonce: 'test-nonce'
77
+ };
78
+
79
+ const proof = await runtime.createProof(testData, session);
80
+
81
+ expect(proof).toBeDefined();
82
+ expect(proof.did).toBe('did:key:zmock123');
83
+ expect(proof.sessionId).toBe('session123');
84
+ expect(proof.audience).toBe('https://client.example.com');
85
+ });
86
+
87
+ it('should prioritize clientDid over userDid when both provided', async () => {
88
+ const testData = { result: 'success' };
89
+ const session = {
90
+ id: 'session123',
91
+ audience: 'https://client.example.com',
92
+ nonce: 'test-nonce',
93
+ clientDid: 'did:key:zclient123',
94
+ userDid: 'did:key:zuser456'
95
+ };
96
+
97
+ const proof = await runtime.createProof(testData, session);
98
+
99
+ expect(proof).toBeDefined();
100
+ expect(proof.sessionId).toBe('session123');
101
+ // clientDid takes precedence in session context
102
+ });
103
+ });
104
+
105
+ describe('processToolCall with clientDid', () => {
106
+ const mockHandler = vi.fn().mockResolvedValue({ result: 'success' });
107
+
108
+ it('should include clientDid in proof when session has clientDid', async () => {
109
+ const session = {
110
+ id: 'session123',
111
+ audience: 'https://client.example.com',
112
+ nonce: 'test-nonce',
113
+ clientDid: 'did:key:zclient123'
114
+ };
115
+
116
+ await runtime.processToolCall(
117
+ 'testTool',
118
+ { arg: 'value' },
119
+ mockHandler,
120
+ session
121
+ );
122
+
123
+ const proof = runtime.getLastProof();
124
+ expect(proof).toBeDefined();
125
+ expect(proof.sessionId).toBe('session123');
126
+ expect(proof.audience).toBe('https://client.example.com');
127
+ });
128
+
129
+ it('should extract clientDid from handshake session', async () => {
130
+ // Create session via handshake
131
+ const handshakeResponse = await runtime.handleHandshake({
132
+ clientDid: 'did:key:zhandshake123',
133
+ audience: 'https://client.example.com'
134
+ });
135
+
136
+ const session = await runtime.getCurrentSession();
137
+ const proof = await runtime.createProof(
138
+ { test: 'data' },
139
+ session
140
+ );
141
+
142
+ expect(proof.sessionId).toBe(handshakeResponse.sessionId);
143
+ expect(session.clientDid).toBe('did:key:zhandshake123');
144
+ });
145
+
146
+ it('should handle userDid from handshake session', async () => {
147
+ // Initialize runtime with user DID generation enabled
148
+ const configWithUserDid = {
149
+ ...config,
150
+ identity: {
151
+ enabled: true,
152
+ generateUserDids: true,
153
+ environment: 'development'
154
+ }
155
+ };
156
+ const runtimeWithUserDid = new MCPIRuntimeBase(configWithUserDid);
157
+ await runtimeWithUserDid.initialize();
158
+
159
+ const handshakeResponse = await runtimeWithUserDid.handleHandshake({
160
+ audience: 'https://client.example.com'
161
+ });
162
+
163
+ const session = await runtimeWithUserDid.getCurrentSession();
164
+ expect(session).toBeDefined();
165
+
166
+ // When userDid generation is enabled, it should be generated and available
167
+ // This is a critical assertion - if userDid is missing, the test should fail
168
+ expect(session.userDid).toBeDefined();
169
+ expect(session.userDid).toMatch(/^did:key:z/);
170
+
171
+ const proof = await runtimeWithUserDid.createProof(
172
+ { test: 'data' },
173
+ session
174
+ );
175
+ expect(proof.sessionId).toBe(handshakeResponse.sessionId);
176
+ });
177
+ });
178
+
179
+ describe('verifyProof with clientDid context', () => {
180
+ let validProof: any;
181
+ const testData = { foo: 'bar' };
182
+
183
+ beforeEach(async () => {
184
+ // Set up DID document for verification
185
+ const fetchProvider = mockProviders.fetchProvider as any;
186
+ fetchProvider.setDIDDocument('did:key:zmock123', {
187
+ verificationMethod: [{
188
+ publicKeyBase64: 'mock-public-key'
189
+ }]
190
+ });
191
+
192
+ const session = {
193
+ id: 'session123',
194
+ audience: 'https://client.example.com',
195
+ nonce: 'test-nonce',
196
+ clientDid: 'did:key:zclient123'
197
+ };
198
+
199
+ validProof = await runtime.createProof(testData, session);
200
+ });
201
+
202
+ it('should verify proof with clientDid in session context', async () => {
203
+ const nonceCache = mockProviders.nonceCacheProvider as any;
204
+ nonceCache.clear();
205
+
206
+ const isValid = await runtime.verifyProof(testData, validProof);
207
+ expect(isValid).toBe(true);
208
+ });
209
+
210
+ it('should verify proof independently of clientDid presence', async () => {
211
+ // Create proof without clientDid
212
+ const proofWithoutClientDid = await runtime.createProof(testData, {
213
+ id: 'session456',
214
+ audience: 'https://client.example.com',
215
+ nonce: 'other-nonce'
216
+ });
217
+
218
+ const nonceCache = mockProviders.nonceCacheProvider as any;
219
+ nonceCache.clear();
220
+
221
+ const isValid = await runtime.verifyProof(testData, proofWithoutClientDid);
222
+ expect(isValid).toBe(true);
223
+ });
224
+ });
225
+
226
+ describe('clientDid extraction from session', () => {
227
+ it('should retrieve clientDid from active session', async () => {
228
+ const handshakeResponse = await runtime.handleHandshake({
229
+ clientDid: 'did:key:zclient789',
230
+ audience: 'https://client.example.com'
231
+ });
232
+
233
+ const session = await runtime.getCurrentSession();
234
+ expect(session.clientDid).toBe('did:key:zclient789');
235
+ });
236
+
237
+ it('should use generated userDid as clientDid when no clientDid provided', async () => {
238
+ const configWithUserDid = {
239
+ ...config,
240
+ identity: {
241
+ enabled: true,
242
+ generateUserDids: true,
243
+ environment: 'development'
244
+ }
245
+ };
246
+ const runtimeWithUserDid = new MCPIRuntimeBase(configWithUserDid);
247
+ await runtimeWithUserDid.initialize();
248
+
249
+ const handshakeResponse = await runtimeWithUserDid.handleHandshake({
250
+ audience: 'https://client.example.com'
251
+ });
252
+
253
+ const session = await runtimeWithUserDid.getCurrentSession();
254
+ expect(session).toBeDefined();
255
+
256
+ // When userDid generation is enabled, it should be generated and available
257
+ // This is a critical assertion - if userDid is missing, the test should fail
258
+ expect(session.userDid).toBeDefined();
259
+ expect(session.userDid).toMatch(/^did:key:z/);
260
+
261
+ // clientDid should be set to userDid if no explicit clientDid provided
262
+ expect(session.clientDid || session.userDid).toBeDefined();
263
+
264
+ // Verify proof includes the session
265
+ const proof = await runtimeWithUserDid.createProof(
266
+ { test: 'data' },
267
+ session
268
+ );
269
+ expect(proof.sessionId).toBe(handshakeResponse.sessionId);
270
+ });
271
+ });
272
+
273
+ describe('edge cases', () => {
274
+ it('should handle empty clientDid string gracefully', async () => {
275
+ const session = {
276
+ id: 'session123',
277
+ audience: 'https://client.example.com',
278
+ nonce: 'test-nonce',
279
+ clientDid: ''
280
+ };
281
+
282
+ const proof = await runtime.createProof({ test: 'data' }, session);
283
+ expect(proof).toBeDefined();
284
+ expect(proof.sessionId).toBe('session123');
285
+ });
286
+
287
+ it('should handle null clientDid gracefully', async () => {
288
+ const session = {
289
+ id: 'session123',
290
+ audience: 'https://client.example.com',
291
+ nonce: 'test-nonce',
292
+ clientDid: null as any
293
+ };
294
+
295
+ const proof = await runtime.createProof({ test: 'data' }, session);
296
+ expect(proof).toBeDefined();
297
+ expect(proof.sessionId).toBe('session123');
298
+ });
299
+
300
+ it('should handle undefined clientDid gracefully', async () => {
301
+ const session = {
302
+ id: 'session123',
303
+ audience: 'https://client.example.com',
304
+ nonce: 'test-nonce'
305
+ // clientDid is undefined
306
+ };
307
+
308
+ const proof = await runtime.createProof({ test: 'data' }, session);
309
+ expect(proof).toBeDefined();
310
+ expect(proof.sessionId).toBe('session123');
311
+ });
312
+
313
+ it('should handle invalid DID format in clientDid', async () => {
314
+ const session = {
315
+ id: 'session123',
316
+ audience: 'https://client.example.com',
317
+ nonce: 'test-nonce',
318
+ clientDid: 'invalid-did-format'
319
+ };
320
+
321
+ const proof = await runtime.createProof({ test: 'data' }, session);
322
+ expect(proof).toBeDefined();
323
+ // Proof should still be created even with invalid DID format
324
+ expect(proof.sessionId).toBe('session123');
325
+ });
326
+ });
327
+
328
+ describe('proof metadata consistency', () => {
329
+ it('should maintain consistent proof structure with clientDid', async () => {
330
+ const session = {
331
+ id: 'session123',
332
+ audience: 'https://client.example.com',
333
+ nonce: 'test-nonce',
334
+ clientDid: 'did:key:zclient123'
335
+ };
336
+
337
+ const proof1 = await runtime.createProof({ test: 'data1' }, session);
338
+ const proof2 = await runtime.createProof({ test: 'data2' }, session);
339
+
340
+ // Both proofs should have same sessionId and audience
341
+ expect(proof1.sessionId).toBe(proof2.sessionId);
342
+ expect(proof1.audience).toBe(proof2.audience);
343
+
344
+ // But different nonces (if not pre-provided)
345
+ // Since we're using same nonce, they should be same
346
+ expect(proof1.nonce).toBe(proof2.nonce);
347
+
348
+ // Different timestamps
349
+ expect(proof1.timestamp).toBeLessThanOrEqual(proof2.timestamp);
350
+ });
351
+
352
+ it('should use different proofs for different sessions', async () => {
353
+ const session1 = {
354
+ id: 'session1',
355
+ audience: 'https://client.example.com',
356
+ nonce: 'nonce1',
357
+ clientDid: 'did:key:zclient1'
358
+ };
359
+
360
+ const session2 = {
361
+ id: 'session2',
362
+ audience: 'https://client.example.com',
363
+ nonce: 'nonce2',
364
+ clientDid: 'did:key:zclient2'
365
+ };
366
+
367
+ const proof1 = await runtime.createProof({ test: 'data' }, session1);
368
+ const proof2 = await runtime.createProof({ test: 'data' }, session2);
369
+
370
+ expect(proof1.sessionId).not.toBe(proof2.sessionId);
371
+ expect(proof1.nonce).not.toBe(proof2.nonce);
372
+ });
373
+ });
374
+ });
375
+