@kya-os/mcp-i-core 1.3.12 → 1.3.14

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 (254) hide show
  1. package/dist/config/remote-config.js +9 -12
  2. package/dist/runtime/base.js +11 -0
  3. package/dist/services/access-control.service.js +5 -0
  4. package/dist/services/tool-protection.service.js +17 -8
  5. package/package.json +2 -2
  6. package/.turbo/turbo-build.log +0 -4
  7. package/.turbo/turbo-test$colon$coverage.log +0 -4586
  8. package/.turbo/turbo-test.log +0 -3169
  9. package/COMPLIANCE_IMPROVEMENT_REPORT.md +0 -483
  10. package/Composer 3.md +0 -615
  11. package/GPT-5.md +0 -1169
  12. package/OPUS-plan.md +0 -352
  13. package/PHASE_3_AND_4.1_SUMMARY.md +0 -585
  14. package/PHASE_3_SUMMARY.md +0 -317
  15. package/PHASE_4.1.3_SUMMARY.md +0 -428
  16. package/PHASE_4.1_COMPLETE.md +0 -525
  17. package/PHASE_4_USER_DID_IDENTITY_LINKING_PLAN.md +0 -1240
  18. package/SCHEMA_COMPLIANCE_REPORT.md +0 -275
  19. package/TEST_PLAN.md +0 -571
  20. package/coverage/coverage-final.json +0 -60
  21. package/dist/cache/oauth-config-cache.d.ts.map +0 -1
  22. package/dist/cache/oauth-config-cache.js.map +0 -1
  23. package/dist/cache/tool-protection-cache.d.ts.map +0 -1
  24. package/dist/cache/tool-protection-cache.js.map +0 -1
  25. package/dist/compliance/index.d.ts.map +0 -1
  26. package/dist/compliance/index.js.map +0 -1
  27. package/dist/compliance/schema-registry.d.ts.map +0 -1
  28. package/dist/compliance/schema-registry.js.map +0 -1
  29. package/dist/compliance/schema-verifier.d.ts.map +0 -1
  30. package/dist/compliance/schema-verifier.js.map +0 -1
  31. package/dist/config/remote-config.d.ts.map +0 -1
  32. package/dist/config/remote-config.js.map +0 -1
  33. package/dist/config.d.ts.map +0 -1
  34. package/dist/config.js.map +0 -1
  35. package/dist/delegation/audience-validator.d.ts.map +0 -1
  36. package/dist/delegation/audience-validator.js.map +0 -1
  37. package/dist/delegation/bitstring.d.ts.map +0 -1
  38. package/dist/delegation/bitstring.js.map +0 -1
  39. package/dist/delegation/cascading-revocation.d.ts.map +0 -1
  40. package/dist/delegation/cascading-revocation.js.map +0 -1
  41. package/dist/delegation/delegation-graph.d.ts.map +0 -1
  42. package/dist/delegation/delegation-graph.js.map +0 -1
  43. package/dist/delegation/did-key-resolver.d.ts.map +0 -1
  44. package/dist/delegation/did-key-resolver.js.map +0 -1
  45. package/dist/delegation/index.d.ts.map +0 -1
  46. package/dist/delegation/index.js.map +0 -1
  47. package/dist/delegation/statuslist-manager.d.ts.map +0 -1
  48. package/dist/delegation/statuslist-manager.js.map +0 -1
  49. package/dist/delegation/storage/index.d.ts.map +0 -1
  50. package/dist/delegation/storage/index.js.map +0 -1
  51. package/dist/delegation/storage/memory-graph-storage.d.ts.map +0 -1
  52. package/dist/delegation/storage/memory-graph-storage.js.map +0 -1
  53. package/dist/delegation/storage/memory-statuslist-storage.d.ts.map +0 -1
  54. package/dist/delegation/storage/memory-statuslist-storage.js.map +0 -1
  55. package/dist/delegation/utils.d.ts.map +0 -1
  56. package/dist/delegation/utils.js.map +0 -1
  57. package/dist/delegation/vc-issuer.d.ts.map +0 -1
  58. package/dist/delegation/vc-issuer.js.map +0 -1
  59. package/dist/delegation/vc-verifier.d.ts.map +0 -1
  60. package/dist/delegation/vc-verifier.js.map +0 -1
  61. package/dist/identity/idp-token-resolver.d.ts.map +0 -1
  62. package/dist/identity/idp-token-resolver.js.map +0 -1
  63. package/dist/identity/idp-token-storage.interface.d.ts.map +0 -1
  64. package/dist/identity/idp-token-storage.interface.js.map +0 -1
  65. package/dist/identity/user-did-manager.d.ts.map +0 -1
  66. package/dist/identity/user-did-manager.js.map +0 -1
  67. package/dist/index.d.ts.map +0 -1
  68. package/dist/index.js.map +0 -1
  69. package/dist/providers/base.d.ts.map +0 -1
  70. package/dist/providers/base.js.map +0 -1
  71. package/dist/providers/memory.d.ts.map +0 -1
  72. package/dist/providers/memory.js.map +0 -1
  73. package/dist/runtime/audit-logger.d.ts.map +0 -1
  74. package/dist/runtime/audit-logger.js.map +0 -1
  75. package/dist/runtime/base.d.ts.map +0 -1
  76. package/dist/runtime/base.js.map +0 -1
  77. package/dist/services/access-control.service.d.ts.map +0 -1
  78. package/dist/services/access-control.service.js.map +0 -1
  79. package/dist/services/authorization/authorization-registry.d.ts.map +0 -1
  80. package/dist/services/authorization/authorization-registry.js.map +0 -1
  81. package/dist/services/authorization/types.d.ts.map +0 -1
  82. package/dist/services/authorization/types.js.map +0 -1
  83. package/dist/services/batch-delegation.service.d.ts.map +0 -1
  84. package/dist/services/batch-delegation.service.js.map +0 -1
  85. package/dist/services/crypto.service.d.ts.map +0 -1
  86. package/dist/services/crypto.service.js.map +0 -1
  87. package/dist/services/errors.d.ts.map +0 -1
  88. package/dist/services/errors.js.map +0 -1
  89. package/dist/services/index.d.ts.map +0 -1
  90. package/dist/services/index.js.map +0 -1
  91. package/dist/services/oauth-config.service.d.ts.map +0 -1
  92. package/dist/services/oauth-config.service.js.map +0 -1
  93. package/dist/services/oauth-provider-registry.d.ts.map +0 -1
  94. package/dist/services/oauth-provider-registry.js.map +0 -1
  95. package/dist/services/oauth-service.d.ts.map +0 -1
  96. package/dist/services/oauth-service.js.map +0 -1
  97. package/dist/services/oauth-token-retrieval.service.d.ts.map +0 -1
  98. package/dist/services/oauth-token-retrieval.service.js.map +0 -1
  99. package/dist/services/proof-verifier.d.ts.map +0 -1
  100. package/dist/services/proof-verifier.js.map +0 -1
  101. package/dist/services/provider-resolver.d.ts.map +0 -1
  102. package/dist/services/provider-resolver.js.map +0 -1
  103. package/dist/services/provider-validator.d.ts.map +0 -1
  104. package/dist/services/provider-validator.js.map +0 -1
  105. package/dist/services/session-registration.service.d.ts.map +0 -1
  106. package/dist/services/session-registration.service.js.map +0 -1
  107. package/dist/services/storage.service.d.ts.map +0 -1
  108. package/dist/services/storage.service.js.map +0 -1
  109. package/dist/services/tool-context-builder.d.ts.map +0 -1
  110. package/dist/services/tool-context-builder.js.map +0 -1
  111. package/dist/services/tool-protection.service.d.ts.map +0 -1
  112. package/dist/services/tool-protection.service.js.map +0 -1
  113. package/dist/types/oauth-required-error.d.ts.map +0 -1
  114. package/dist/types/oauth-required-error.js.map +0 -1
  115. package/dist/types/tool-protection.d.ts.map +0 -1
  116. package/dist/types/tool-protection.js.map +0 -1
  117. package/dist/utils/base58.d.ts.map +0 -1
  118. package/dist/utils/base58.js.map +0 -1
  119. package/dist/utils/base64.d.ts.map +0 -1
  120. package/dist/utils/base64.js.map +0 -1
  121. package/dist/utils/cors.d.ts.map +0 -1
  122. package/dist/utils/cors.js.map +0 -1
  123. package/dist/utils/did-helpers.d.ts.map +0 -1
  124. package/dist/utils/did-helpers.js.map +0 -1
  125. package/dist/utils/index.d.ts.map +0 -1
  126. package/dist/utils/index.js.map +0 -1
  127. package/dist/utils/storage-keys.d.ts.map +0 -1
  128. package/dist/utils/storage-keys.js.map +0 -1
  129. package/docs/API_REFERENCE.md +0 -1362
  130. package/docs/COMPLIANCE_MATRIX.md +0 -691
  131. package/docs/STATUSLIST2021_GUIDE.md +0 -696
  132. package/docs/W3C_VC_DELEGATION_GUIDE.md +0 -710
  133. package/src/__tests__/cache/tool-protection-cache.test.ts +0 -640
  134. package/src/__tests__/config/provider-runtime-config.test.ts +0 -309
  135. package/src/__tests__/delegation-e2e.test.ts +0 -690
  136. package/src/__tests__/identity/user-did-manager.test.ts +0 -232
  137. package/src/__tests__/index.test.ts +0 -56
  138. package/src/__tests__/integration/full-flow.test.ts +0 -789
  139. package/src/__tests__/integration.test.ts +0 -281
  140. package/src/__tests__/providers/base.test.ts +0 -173
  141. package/src/__tests__/providers/memory.test.ts +0 -319
  142. package/src/__tests__/regression/phase2-regression.test.ts +0 -429
  143. package/src/__tests__/runtime/audit-logger.test.ts +0 -154
  144. package/src/__tests__/runtime/base-extensions.test.ts +0 -595
  145. package/src/__tests__/runtime/base.test.ts +0 -869
  146. package/src/__tests__/runtime/delegation-flow.test.ts +0 -164
  147. package/src/__tests__/runtime/proof-client-did.test.ts +0 -376
  148. package/src/__tests__/runtime/route-interception.test.ts +0 -686
  149. package/src/__tests__/runtime/tool-protection-enforcement.test.ts +0 -908
  150. package/src/__tests__/services/agentshield-integration.test.ts +0 -791
  151. package/src/__tests__/services/cache-busting.test.ts +0 -125
  152. package/src/__tests__/services/oauth-service-pkce.test.ts +0 -556
  153. package/src/__tests__/services/provider-resolver-edge-cases.test.ts +0 -591
  154. package/src/__tests__/services/tool-protection-merged-config.test.ts +0 -485
  155. package/src/__tests__/services/tool-protection-oauth-provider.test.ts +0 -480
  156. package/src/__tests__/services/tool-protection.service.test.ts +0 -1373
  157. package/src/__tests__/utils/mock-providers.ts +0 -340
  158. package/src/cache/oauth-config-cache.d.ts +0 -69
  159. package/src/cache/oauth-config-cache.d.ts.map +0 -1
  160. package/src/cache/oauth-config-cache.js.map +0 -1
  161. package/src/cache/oauth-config-cache.ts +0 -123
  162. package/src/cache/tool-protection-cache.ts +0 -171
  163. package/src/compliance/EXAMPLE.md +0 -412
  164. package/src/compliance/__tests__/schema-verifier.test.ts +0 -797
  165. package/src/compliance/index.ts +0 -8
  166. package/src/compliance/schema-registry.ts +0 -460
  167. package/src/compliance/schema-verifier.ts +0 -708
  168. package/src/config/__tests__/merged-config.spec.ts +0 -445
  169. package/src/config/__tests__/remote-config.spec.ts +0 -268
  170. package/src/config/remote-config.ts +0 -264
  171. package/src/config.ts +0 -312
  172. package/src/delegation/__tests__/audience-validator.test.ts +0 -112
  173. package/src/delegation/__tests__/bitstring.test.ts +0 -346
  174. package/src/delegation/__tests__/cascading-revocation.test.ts +0 -628
  175. package/src/delegation/__tests__/delegation-graph.test.ts +0 -584
  176. package/src/delegation/__tests__/did-key-resolver.test.ts +0 -265
  177. package/src/delegation/__tests__/utils.test.ts +0 -152
  178. package/src/delegation/__tests__/vc-issuer.test.ts +0 -442
  179. package/src/delegation/__tests__/vc-verifier.test.ts +0 -922
  180. package/src/delegation/audience-validator.ts +0 -52
  181. package/src/delegation/bitstring.ts +0 -278
  182. package/src/delegation/cascading-revocation.ts +0 -370
  183. package/src/delegation/delegation-graph.ts +0 -299
  184. package/src/delegation/did-key-resolver.ts +0 -179
  185. package/src/delegation/index.ts +0 -14
  186. package/src/delegation/statuslist-manager.ts +0 -353
  187. package/src/delegation/storage/__tests__/memory-graph-storage.test.ts +0 -366
  188. package/src/delegation/storage/__tests__/memory-statuslist-storage.test.ts +0 -228
  189. package/src/delegation/storage/index.ts +0 -9
  190. package/src/delegation/storage/memory-graph-storage.ts +0 -178
  191. package/src/delegation/storage/memory-statuslist-storage.ts +0 -77
  192. package/src/delegation/utils.ts +0 -221
  193. package/src/delegation/vc-issuer.ts +0 -232
  194. package/src/delegation/vc-verifier.ts +0 -568
  195. package/src/identity/idp-token-resolver.ts +0 -181
  196. package/src/identity/idp-token-storage.interface.ts +0 -94
  197. package/src/identity/user-did-manager.ts +0 -526
  198. package/src/index.ts +0 -310
  199. package/src/providers/base.d.ts +0 -91
  200. package/src/providers/base.d.ts.map +0 -1
  201. package/src/providers/base.js.map +0 -1
  202. package/src/providers/base.ts +0 -96
  203. package/src/providers/memory.ts +0 -142
  204. package/src/runtime/audit-logger.ts +0 -39
  205. package/src/runtime/base.ts +0 -1392
  206. package/src/services/__tests__/access-control.integration.test.ts +0 -443
  207. package/src/services/__tests__/access-control.proof-response-validation.test.ts +0 -578
  208. package/src/services/__tests__/access-control.service.test.ts +0 -970
  209. package/src/services/__tests__/batch-delegation.service.test.ts +0 -351
  210. package/src/services/__tests__/crypto.service.test.ts +0 -531
  211. package/src/services/__tests__/oauth-provider-registry.test.ts +0 -142
  212. package/src/services/__tests__/proof-verifier.integration.test.ts +0 -485
  213. package/src/services/__tests__/proof-verifier.test.ts +0 -489
  214. package/src/services/__tests__/provider-resolution.integration.test.ts +0 -202
  215. package/src/services/__tests__/provider-resolver.test.ts +0 -213
  216. package/src/services/__tests__/storage.service.test.ts +0 -358
  217. package/src/services/access-control.service.ts +0 -990
  218. package/src/services/authorization/authorization-registry.ts +0 -66
  219. package/src/services/authorization/types.ts +0 -71
  220. package/src/services/batch-delegation.service.ts +0 -137
  221. package/src/services/crypto.service.ts +0 -302
  222. package/src/services/errors.ts +0 -76
  223. package/src/services/index.ts +0 -18
  224. package/src/services/oauth-config.service.d.ts +0 -53
  225. package/src/services/oauth-config.service.d.ts.map +0 -1
  226. package/src/services/oauth-config.service.js.map +0 -1
  227. package/src/services/oauth-config.service.ts +0 -192
  228. package/src/services/oauth-provider-registry.d.ts +0 -57
  229. package/src/services/oauth-provider-registry.d.ts.map +0 -1
  230. package/src/services/oauth-provider-registry.js.map +0 -1
  231. package/src/services/oauth-provider-registry.ts +0 -141
  232. package/src/services/oauth-service.ts +0 -544
  233. package/src/services/oauth-token-retrieval.service.ts +0 -245
  234. package/src/services/proof-verifier.ts +0 -478
  235. package/src/services/provider-resolver.d.ts +0 -48
  236. package/src/services/provider-resolver.d.ts.map +0 -1
  237. package/src/services/provider-resolver.js.map +0 -1
  238. package/src/services/provider-resolver.ts +0 -146
  239. package/src/services/provider-validator.ts +0 -170
  240. package/src/services/session-registration.service.ts +0 -251
  241. package/src/services/storage.service.ts +0 -566
  242. package/src/services/tool-context-builder.ts +0 -237
  243. package/src/services/tool-protection.service.ts +0 -1070
  244. package/src/types/oauth-required-error.ts +0 -63
  245. package/src/types/tool-protection.ts +0 -155
  246. package/src/utils/__tests__/did-helpers.test.ts +0 -156
  247. package/src/utils/base58.ts +0 -109
  248. package/src/utils/base64.ts +0 -148
  249. package/src/utils/cors.ts +0 -83
  250. package/src/utils/did-helpers.ts +0 -210
  251. package/src/utils/index.ts +0 -8
  252. package/src/utils/storage-keys.ts +0 -278
  253. package/tsconfig.json +0 -21
  254. package/vitest.config.ts +0 -56
@@ -1,489 +0,0 @@
1
- /**
2
- * Tests for ProofVerifier
3
- *
4
- * Comprehensive security test coverage for proof verification service.
5
- * Tests nonce replay protection, timestamp skew validation, canonical payload reconstruction,
6
- * and various security attack scenarios.
7
- *
8
- * Test Coverage Requirements: 100% - All security-critical code paths
9
- */
10
-
11
- import { describe, it, expect, beforeEach, vi } from 'vitest';
12
- import { ProofVerifier } from '../proof-verifier.js';
13
- import { CryptoService, type Ed25519JWK } from '../crypto.service.js';
14
- import type {
15
- CryptoProvider,
16
- ClockProvider,
17
- NonceCacheProvider,
18
- FetchProvider,
19
- } from '../../providers/base.js';
20
- import type { DetachedProof } from '@kya-os/contracts/proof';
21
- import {
22
- ProofVerificationError,
23
- PROOF_VERIFICATION_ERROR_CODES,
24
- } from '../errors.js';
25
-
26
- describe('ProofVerifier Security', () => {
27
- let proofVerifier: ProofVerifier;
28
- let mockCryptoProvider: CryptoProvider;
29
- let mockClockProvider: ClockProvider;
30
- let mockNonceCache: NonceCacheProvider;
31
- let mockFetchProvider: FetchProvider;
32
- let cryptoService: CryptoService;
33
-
34
- const validJwk: Ed25519JWK = {
35
- kty: 'OKP',
36
- crv: 'Ed25519',
37
- x: 'VCpo2LMLhn6iWku8MKvSLg2ZAoC-nlOyPVQaO3FxVeQ',
38
- kid: 'did:key:z123#key-1',
39
- };
40
-
41
- const createValidProof = (): DetachedProof => {
42
- const header = { alg: 'EdDSA', typ: 'JWT' };
43
- // Create a proper JSON payload that matches the meta structure
44
- const payload = {
45
- aud: 'test-audience',
46
- sub: 'did:key:z123',
47
- iss: 'did:key:z123',
48
- nonce: 'nonce123',
49
- ts: Math.floor(Date.now() / 1000),
50
- sessionId: 'session123',
51
- requestHash: 'sha256:' + 'a'.repeat(64),
52
- responseHash: 'sha256:' + 'b'.repeat(64),
53
- };
54
- // Use btoa for base64 encoding (available in test environment via polyfill)
55
- const headerB64 = btoa(JSON.stringify(header))
56
- .replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
57
- const payloadB64 = btoa(JSON.stringify(payload))
58
- .replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
59
- const signatureB64 = btoa('signature')
60
- .replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
61
- const jws = `${headerB64}.${payloadB64}.${signatureB64}`;
62
-
63
- return {
64
- jws,
65
- meta: {
66
- did: 'did:key:z123',
67
- kid: 'did:key:z123#key-1',
68
- ts: Math.floor(Date.now() / 1000),
69
- nonce: 'nonce123',
70
- audience: 'test-audience',
71
- sessionId: 'session123',
72
- requestHash: 'sha256:' + 'a'.repeat(64),
73
- responseHash: 'sha256:' + 'b'.repeat(64),
74
- },
75
- };
76
- };
77
-
78
- beforeEach(() => {
79
- mockCryptoProvider = {
80
- sign: vi.fn(),
81
- verify: vi.fn().mockResolvedValue(true),
82
- generateKeyPair: vi.fn(),
83
- hash: vi.fn(),
84
- randomBytes: vi.fn(),
85
- };
86
-
87
- cryptoService = new CryptoService(mockCryptoProvider);
88
-
89
- mockClockProvider = {
90
- now: vi.fn().mockReturnValue(Date.now()), // Return milliseconds
91
- isWithinSkew: vi.fn().mockReturnValue(true),
92
- hasExpired: vi.fn(),
93
- calculateExpiry: vi.fn((ttlSeconds: number) => Date.now() + (ttlSeconds * 1000)), // Return milliseconds
94
- format: vi.fn(),
95
- };
96
-
97
- mockNonceCache = {
98
- has: vi.fn().mockResolvedValue(false),
99
- add: vi.fn().mockResolvedValue(undefined),
100
- cleanup: vi.fn().mockResolvedValue(undefined),
101
- destroy: vi.fn().mockResolvedValue(undefined),
102
- };
103
-
104
- mockFetchProvider = {
105
- resolveDID: vi.fn().mockResolvedValue({
106
- verificationMethod: [{
107
- id: 'did:key:z123#key-1',
108
- publicKeyJwk: validJwk,
109
- }],
110
- }),
111
- fetchStatusList: vi.fn(),
112
- fetchDelegationChain: vi.fn(),
113
- fetch: vi.fn(),
114
- };
115
-
116
- proofVerifier = new ProofVerifier({
117
- cryptoProvider: mockCryptoProvider,
118
- clockProvider: mockClockProvider,
119
- nonceCacheProvider: mockNonceCache,
120
- fetchProvider: mockFetchProvider,
121
- timestampSkewSeconds: 120,
122
- nonceTtlSeconds: 300,
123
- });
124
- });
125
-
126
- describe('Nonce Replay Protection', () => {
127
- it('should prevent nonce replay attacks', async () => {
128
- const proof = createValidProof();
129
-
130
- // First verification should succeed
131
- const result1 = await proofVerifier.verifyProof(proof, validJwk);
132
- expect(result1.valid).toBe(true);
133
- expect(mockNonceCache.has).toHaveBeenCalledWith('nonce123', 'did:key:z123');
134
- expect(mockNonceCache.add).toHaveBeenCalled();
135
-
136
- // Reset mock to simulate second attempt
137
- mockNonceCache.has = vi.fn().mockResolvedValue(true);
138
-
139
- // Second verification with same nonce should fail
140
- const result2 = await proofVerifier.verifyProof(proof, validJwk);
141
- expect(result2.valid).toBe(false);
142
- expect(result2.reason).toContain('replay');
143
- });
144
-
145
- it('should add nonce to cache after successful verification', async () => {
146
- const proof = createValidProof();
147
-
148
- await proofVerifier.verifyProof(proof, validJwk);
149
-
150
- expect(mockNonceCache.add).toHaveBeenCalledWith(
151
- 'nonce123',
152
- expect.any(Number),
153
- 'did:key:z123'
154
- );
155
- });
156
- });
157
-
158
- describe('Timestamp Skew Validation', () => {
159
- it('should enforce timestamp skew limits', async () => {
160
- const proof = createValidProof();
161
- const currentTime = Date.now(); // milliseconds
162
-
163
- // Set clock to 5 minutes in the future
164
- mockClockProvider.now = vi.fn().mockReturnValue(currentTime);
165
- mockClockProvider.isWithinSkew = vi.fn().mockReturnValue(false);
166
-
167
- const result = await proofVerifier.verifyProof(proof, validJwk);
168
-
169
- expect(result.valid).toBe(false);
170
- expect(result.reason).toContain('skew');
171
- // isWithinSkew is called with timestamp in milliseconds (converted from seconds)
172
- expect(mockClockProvider.isWithinSkew).toHaveBeenCalledWith(
173
- proof.meta.ts * 1000, // Convert seconds to milliseconds
174
- 120
175
- );
176
- });
177
-
178
- it('should accept timestamps within skew window', async () => {
179
- const proof = createValidProof();
180
- mockClockProvider.isWithinSkew = vi.fn().mockReturnValue(true);
181
-
182
- const result = await proofVerifier.verifyProof(proof, validJwk);
183
-
184
- expect(result.valid).toBe(true);
185
- });
186
-
187
- it('should use custom timestamp skew seconds', async () => {
188
- const customProofVerifier = new ProofVerifier({
189
- cryptoProvider: mockCryptoProvider,
190
- clockProvider: mockClockProvider,
191
- nonceCacheProvider: mockNonceCache,
192
- fetchProvider: mockFetchProvider,
193
- timestampSkewSeconds: 300, // 5 minutes
194
- nonceTtlSeconds: 300,
195
- });
196
-
197
- const proof = createValidProof();
198
- mockClockProvider.isWithinSkew = vi.fn().mockReturnValue(false);
199
-
200
- await customProofVerifier.verifyProof(proof, validJwk);
201
-
202
- // isWithinSkew is called with timestamp in milliseconds (converted from seconds)
203
- expect(mockClockProvider.isWithinSkew).toHaveBeenCalledWith(
204
- proof.meta.ts * 1000, // Convert seconds to milliseconds
205
- 300
206
- );
207
- });
208
- });
209
-
210
- describe('Canonical Payload Reconstruction', () => {
211
- it('should reconstruct canonical payload from meta', async () => {
212
- const proof = createValidProof();
213
-
214
- await proofVerifier.verifyProof(proof, validJwk);
215
-
216
- // Verify that verifyJWS was called with detached payload
217
- expect(mockCryptoProvider.verify).toHaveBeenCalled();
218
- });
219
-
220
- it('should validate canonical payload ordering determinism', () => {
221
- const meta1 = {
222
- z: 1,
223
- a: 2,
224
- m: 3,
225
- did: 'did:test',
226
- kid: 'kid',
227
- ts: 123,
228
- nonce: 'nonce',
229
- audience: 'aud',
230
- sessionId: 'session',
231
- requestHash: 'sha256:' + 'a'.repeat(64),
232
- responseHash: 'sha256:' + 'b'.repeat(64),
233
- };
234
- const meta2 = {
235
- a: 2,
236
- m: 3,
237
- z: 1,
238
- did: 'did:test',
239
- kid: 'kid',
240
- ts: 123,
241
- nonce: 'nonce',
242
- audience: 'aud',
243
- sessionId: 'session',
244
- requestHash: 'sha256:' + 'a'.repeat(64),
245
- responseHash: 'sha256:' + 'b'.repeat(64),
246
- };
247
-
248
- const canonical1 = proofVerifier.buildCanonicalPayload(meta1);
249
- const canonical2 = proofVerifier.buildCanonicalPayload(meta2);
250
-
251
- // Should be identical despite different key order
252
- expect(canonical1).toBe(canonical2);
253
- });
254
-
255
- it('should handle detached JWS reconstruction', async () => {
256
- const header = { alg: 'EdDSA' };
257
- const headerB64 = btoa(JSON.stringify(header))
258
- .replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
259
- const signatureB64 = btoa('signature')
260
- .replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
261
- const detachedJws = `${headerB64}..${signatureB64}`;
262
-
263
- const proof: DetachedProof = {
264
- jws: detachedJws,
265
- meta: createValidProof().meta,
266
- };
267
-
268
- const result = await proofVerifier.verifyProof(proof, validJwk);
269
-
270
- // Should call verifyJWS with detached payload
271
- expect(mockCryptoProvider.verify).toHaveBeenCalled();
272
- expect(result.valid).toBe(true);
273
- });
274
- });
275
-
276
- describe('Proof Structure Validation', () => {
277
- it('should reject invalid proof structure', async () => {
278
- const invalidProof = {
279
- jws: 'invalid',
280
- meta: {
281
- // Missing required fields
282
- did: 'did:test',
283
- },
284
- } as any;
285
-
286
- const result = await proofVerifier.verifyProof(invalidProof, validJwk);
287
-
288
- expect(result.valid).toBe(false);
289
- expect(result.reason).toContain('Invalid proof structure');
290
- });
291
-
292
- it('should reject proof with missing required meta fields', async () => {
293
- const invalidProof: DetachedProof = {
294
- jws: 'header.payload.signature',
295
- meta: {
296
- did: 'did:test',
297
- kid: 'kid',
298
- ts: 123,
299
- nonce: 'nonce',
300
- audience: 'aud',
301
- sessionId: 'session',
302
- // Missing requestHash and responseHash
303
- requestHash: '' as any,
304
- responseHash: '' as any,
305
- },
306
- };
307
-
308
- const result = await proofVerifier.verifyProof(invalidProof, validJwk);
309
-
310
- expect(result.valid).toBe(false);
311
- });
312
- });
313
-
314
- describe('Signature Verification', () => {
315
- it('should reject proof with invalid signature', async () => {
316
- const proof = createValidProof();
317
- mockCryptoProvider.verify = vi.fn().mockResolvedValue(false);
318
-
319
- const result = await proofVerifier.verifyProof(proof, validJwk);
320
-
321
- expect(result.valid).toBe(false);
322
- expect(result.reason).toContain('Invalid JWS signature');
323
- });
324
-
325
- it('should handle signature verification errors gracefully', async () => {
326
- const proof = createValidProof();
327
- mockCryptoProvider.verify = vi.fn().mockRejectedValue(
328
- new Error('Crypto error')
329
- );
330
-
331
- const result = await proofVerifier.verifyProof(proof, validJwk);
332
-
333
- expect(result.valid).toBe(false);
334
- expect(result.reason).toBeDefined();
335
- // Should not throw, should return error result
336
- });
337
- });
338
-
339
- describe('verifyProofDetached', () => {
340
- it('should verify proof with string canonical payload', async () => {
341
- const proof = createValidProof();
342
- const canonicalPayload = proofVerifier.buildCanonicalPayload(proof.meta);
343
-
344
- const result = await proofVerifier.verifyProofDetached(
345
- proof,
346
- canonicalPayload,
347
- validJwk
348
- );
349
-
350
- expect(result.valid).toBe(true);
351
- });
352
-
353
- it('should verify proof with Uint8Array canonical payload', async () => {
354
- const proof = createValidProof();
355
- const canonicalPayload = proofVerifier.buildCanonicalPayload(proof.meta);
356
- const canonicalPayloadBytes = new TextEncoder().encode(canonicalPayload);
357
-
358
- const result = await proofVerifier.verifyProofDetached(
359
- proof,
360
- canonicalPayloadBytes,
361
- validJwk
362
- );
363
-
364
- expect(result.valid).toBe(true);
365
- });
366
-
367
- it('should prevent nonce replay in verifyProofDetached', async () => {
368
- const proof = createValidProof();
369
- const canonicalPayload = proofVerifier.buildCanonicalPayload(proof.meta);
370
-
371
- // First verification
372
- const result1 = await proofVerifier.verifyProofDetached(
373
- proof,
374
- canonicalPayload,
375
- validJwk
376
- );
377
- expect(result1.valid).toBe(true);
378
-
379
- // Second verification should fail
380
- mockNonceCache.has = vi.fn().mockResolvedValue(true);
381
- const result2 = await proofVerifier.verifyProofDetached(
382
- proof,
383
- canonicalPayload,
384
- validJwk
385
- );
386
- expect(result2.valid).toBe(false);
387
- expect(result2.reason).toContain('replay');
388
- });
389
- });
390
-
391
- describe('Error Handling', () => {
392
- it('should never throw on verification errors', async () => {
393
- const proof = createValidProof();
394
-
395
- // Simulate various error conditions
396
- mockNonceCache.has = vi.fn().mockRejectedValue(new Error('Cache error'));
397
-
398
- const result = await proofVerifier.verifyProof(proof, validJwk);
399
-
400
- // Should return error result, not throw
401
- expect(result.valid).toBe(false);
402
- expect(result.reason).toBeDefined();
403
- });
404
-
405
- it('should handle clock provider errors gracefully', async () => {
406
- const proof = createValidProof();
407
- mockClockProvider.isWithinSkew = vi.fn().mockImplementation(() => {
408
- throw new Error('Clock error');
409
- });
410
-
411
- const result = await proofVerifier.verifyProof(proof, validJwk);
412
-
413
- expect(result.valid).toBe(false);
414
- expect(result.reason).toBeDefined();
415
- });
416
- });
417
-
418
- describe('fetchPublicKeyFromDID', () => {
419
- it('should fetch public key from DID document', async () => {
420
- const jwk = await proofVerifier.fetchPublicKeyFromDID('did:key:z123', 'key-1');
421
-
422
- expect(jwk).toEqual(validJwk);
423
- expect(mockFetchProvider.resolveDID).toHaveBeenCalledWith('did:key:z123');
424
- });
425
-
426
- it('should throw ProofVerificationError if DID document not found', async () => {
427
- mockFetchProvider.resolveDID = vi.fn().mockResolvedValue(null);
428
-
429
- await expect(
430
- proofVerifier.fetchPublicKeyFromDID('did:key:z123')
431
- ).rejects.toThrow(ProofVerificationError);
432
-
433
- try {
434
- await proofVerifier.fetchPublicKeyFromDID('did:key:z123');
435
- } catch (error) {
436
- expect(error).toBeInstanceOf(ProofVerificationError);
437
- expect((error as ProofVerificationError).code).toBe(
438
- PROOF_VERIFICATION_ERROR_CODES.DID_DOCUMENT_NOT_FOUND
439
- );
440
- }
441
- });
442
-
443
- it('should throw ProofVerificationError if verification method not found', async () => {
444
- mockFetchProvider.resolveDID = vi.fn().mockResolvedValue({
445
- verificationMethod: [],
446
- });
447
-
448
- await expect(
449
- proofVerifier.fetchPublicKeyFromDID('did:key:z123', 'key-1')
450
- ).rejects.toThrow(ProofVerificationError);
451
-
452
- try {
453
- await proofVerifier.fetchPublicKeyFromDID('did:key:z123', 'key-1');
454
- } catch (error) {
455
- expect(error).toBeInstanceOf(ProofVerificationError);
456
- expect((error as ProofVerificationError).code).toBe(
457
- PROOF_VERIFICATION_ERROR_CODES.VERIFICATION_METHOD_NOT_FOUND
458
- );
459
- }
460
- });
461
-
462
- it('should throw ProofVerificationError if JWK is not Ed25519', async () => {
463
- mockFetchProvider.resolveDID = vi.fn().mockResolvedValue({
464
- verificationMethod: [{
465
- id: 'did:key:z123#key-1',
466
- publicKeyJwk: {
467
- kty: 'RSA',
468
- crv: 'RS256',
469
- n: 'invalid',
470
- },
471
- }],
472
- });
473
-
474
- await expect(
475
- proofVerifier.fetchPublicKeyFromDID('did:key:z123')
476
- ).rejects.toThrow(ProofVerificationError);
477
-
478
- try {
479
- await proofVerifier.fetchPublicKeyFromDID('did:key:z123');
480
- } catch (error) {
481
- expect(error).toBeInstanceOf(ProofVerificationError);
482
- expect((error as ProofVerificationError).code).toBe(
483
- PROOF_VERIFICATION_ERROR_CODES.INVALID_JWK_FORMAT
484
- );
485
- }
486
- });
487
- });
488
- });
489
-
@@ -1,202 +0,0 @@
1
- /**
2
- * Provider Resolution Integration Tests
3
- *
4
- * Tests end-to-end provider resolution flow and backward compatibility.
5
- *
6
- * @package @kya-os/mcp-i-core
7
- */
8
-
9
- import { describe, it, expect, beforeEach, vi } from "vitest";
10
- import { OAuthProviderRegistry } from "../oauth-provider-registry.js";
11
- import { ProviderResolver } from "../provider-resolver.js";
12
- import { OAuthConfigService } from "../oauth-config.service.js";
13
- import { ToolProtectionService } from "../tool-protection.service.js";
14
- import type { ToolProtection } from "@kya-os/contracts/tool-protection";
15
- import type { OAuthConfig } from "@kya-os/contracts/config";
16
-
17
- describe("Provider Resolution Integration", () => {
18
- let mockConfigService: OAuthConfigService;
19
- let mockToolProtectionService: ToolProtectionService;
20
- let providerRegistry: OAuthProviderRegistry;
21
- let providerResolver: ProviderResolver;
22
-
23
- const mockOAuthConfig: OAuthConfig = {
24
- providers: {
25
- github: {
26
- clientId: "github_client_id",
27
- authorizationUrl: "https://github.com/login/oauth/authorize",
28
- tokenUrl: "https://github.com/login/oauth/access_token",
29
- supportsPKCE: true,
30
- requiresClientSecret: false,
31
- },
32
- google: {
33
- clientId: "google_client_id",
34
- authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
35
- tokenUrl: "https://oauth2.googleapis.com/token",
36
- supportsPKCE: true,
37
- requiresClientSecret: false,
38
- },
39
- },
40
- // Explicitly configured provider (Priority 3 fallback)
41
- configuredProvider: "github",
42
- };
43
-
44
- beforeEach(() => {
45
- mockConfigService = {
46
- getOAuthConfig: vi.fn().mockResolvedValue(mockOAuthConfig),
47
- } as any;
48
-
49
- mockToolProtectionService = {
50
- checkToolProtection: vi.fn(),
51
- getProjectId: vi.fn().mockReturnValue("test-project"),
52
- } as any;
53
-
54
- providerRegistry = new OAuthProviderRegistry(mockConfigService);
55
- providerResolver = new ProviderResolver(providerRegistry, mockConfigService);
56
- });
57
-
58
- describe("End-to-end provider resolution", () => {
59
- it("should resolve provider for tool with oauthProvider field", async () => {
60
- await providerRegistry.loadFromAgentShield("test-project");
61
-
62
- const toolProtection: ToolProtection = {
63
- requiresDelegation: true,
64
- requiredScopes: ["repo:read"],
65
- oauthProvider: "github",
66
- };
67
-
68
- const provider = await providerResolver.resolveProvider(
69
- toolProtection,
70
- "test-project"
71
- );
72
-
73
- expect(provider).toBe("github");
74
- });
75
-
76
- it("should resolve provider via scope inference when oauthProvider not specified", async () => {
77
- await providerRegistry.loadFromAgentShield("test-project");
78
-
79
- const toolProtection: ToolProtection = {
80
- requiresDelegation: true,
81
- requiredScopes: ["github:repo:read"],
82
- // No oauthProvider field (Phase 1 compatibility)
83
- };
84
-
85
- const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => {});
86
-
87
- const provider = await providerResolver.resolveProvider(
88
- toolProtection,
89
- "test-project"
90
- );
91
-
92
- expect(provider).toBe("github");
93
- expect(consoleSpy).toHaveBeenCalledWith(
94
- expect.stringContaining("Inferred provider")
95
- );
96
-
97
- consoleSpy.mockRestore();
98
- });
99
-
100
- it("should fall back to configuredProvider for Phase 1 compatibility", async () => {
101
- await providerRegistry.loadFromAgentShield("test-project");
102
-
103
- const toolProtection: ToolProtection = {
104
- requiresDelegation: true,
105
- requiredScopes: ["custom:scope"], // No recognizable prefix
106
- // No oauthProvider field
107
- };
108
-
109
- const consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
110
-
111
- const provider = await providerResolver.resolveProvider(
112
- toolProtection,
113
- "test-project"
114
- );
115
-
116
- // Should use configuredProvider (github)
117
- expect(provider).toBe("github");
118
- expect(consoleSpy).toHaveBeenCalledWith(
119
- expect.stringContaining("project-configured provider")
120
- );
121
-
122
- consoleSpy.mockRestore();
123
- });
124
- });
125
-
126
- describe("Backward compatibility", () => {
127
- it("should work with Phase 1 tools (no oauthProvider field)", async () => {
128
- await providerRegistry.loadFromAgentShield("test-project");
129
-
130
- const toolProtection: ToolProtection = {
131
- requiresDelegation: true,
132
- requiredScopes: ["repo:read"],
133
- // No oauthProvider - Phase 1 style
134
- };
135
-
136
- // Should not throw - uses fallback resolution
137
- const provider = await providerResolver.resolveProvider(
138
- toolProtection,
139
- "test-project"
140
- );
141
-
142
- expect(provider).toBeDefined();
143
- expect(typeof provider).toBe("string");
144
- });
145
-
146
- it("should handle tools with empty requiredScopes", async () => {
147
- await providerRegistry.loadFromAgentShield("test-project");
148
-
149
- const toolProtection: ToolProtection = {
150
- requiresDelegation: true,
151
- requiredScopes: [],
152
- // No oauthProvider
153
- };
154
-
155
- const consoleSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
156
-
157
- const provider = await providerResolver.resolveProvider(
158
- toolProtection,
159
- "test-project"
160
- );
161
-
162
- // Should fall back to configuredProvider
163
- expect(provider).toBe("github");
164
- expect(consoleSpy).toHaveBeenCalledWith(
165
- expect.stringContaining("project-configured provider")
166
- );
167
-
168
- consoleSpy.mockRestore();
169
- });
170
- });
171
-
172
- describe("Multi-provider scenarios", () => {
173
- it("should resolve different providers for different tools", async () => {
174
- await providerRegistry.loadFromAgentShield("test-project");
175
-
176
- const githubTool: ToolProtection = {
177
- requiresDelegation: true,
178
- requiredScopes: ["repo:read"],
179
- oauthProvider: "github",
180
- };
181
-
182
- const googleTool: ToolProtection = {
183
- requiresDelegation: true,
184
- requiredScopes: ["calendar:read"],
185
- oauthProvider: "google",
186
- };
187
-
188
- const githubProvider = await providerResolver.resolveProvider(
189
- githubTool,
190
- "test-project"
191
- );
192
- const googleProvider = await providerResolver.resolveProvider(
193
- googleTool,
194
- "test-project"
195
- );
196
-
197
- expect(githubProvider).toBe("github");
198
- expect(googleProvider).toBe("google");
199
- });
200
- });
201
- });
202
-