@kya-os/mcp-i-core 1.3.7-canary.0 → 1.3.7-canary.clientinfo.20251126041014
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.
- package/.turbo/turbo-build.log +4 -0
- package/.turbo/turbo-test$colon$coverage.log +4239 -0
- package/.turbo/turbo-test.log +2973 -0
- package/COMPLIANCE_IMPROVEMENT_REPORT.md +483 -0
- package/Composer 3.md +615 -0
- package/GPT-5.md +1169 -0
- package/OPUS-plan.md +352 -0
- package/PHASE_3_AND_4.1_SUMMARY.md +585 -0
- package/PHASE_3_SUMMARY.md +317 -0
- package/PHASE_4.1.3_SUMMARY.md +428 -0
- package/PHASE_4.1_COMPLETE.md +525 -0
- package/PHASE_4_USER_DID_IDENTITY_LINKING_PLAN.md +1240 -0
- package/SCHEMA_COMPLIANCE_REPORT.md +275 -0
- package/TEST_PLAN.md +571 -0
- package/coverage/coverage-final.json +57 -0
- package/dist/__tests__/utils/mock-providers.d.ts +1 -2
- package/dist/__tests__/utils/mock-providers.d.ts.map +1 -1
- package/dist/__tests__/utils/mock-providers.js.map +1 -1
- package/dist/cache/oauth-config-cache.d.ts +69 -0
- package/dist/cache/oauth-config-cache.d.ts.map +1 -0
- package/dist/cache/oauth-config-cache.js +76 -0
- package/dist/cache/oauth-config-cache.js.map +1 -0
- package/dist/identity/idp-token-resolver.d.ts +53 -0
- package/dist/identity/idp-token-resolver.d.ts.map +1 -0
- package/dist/identity/idp-token-resolver.js +108 -0
- package/dist/identity/idp-token-resolver.js.map +1 -0
- package/dist/identity/idp-token-storage.interface.d.ts +42 -0
- package/dist/identity/idp-token-storage.interface.d.ts.map +1 -0
- package/dist/identity/idp-token-storage.interface.js +12 -0
- package/dist/identity/idp-token-storage.interface.js.map +1 -0
- package/dist/identity/user-did-manager.d.ts +39 -1
- package/dist/identity/user-did-manager.d.ts.map +1 -1
- package/dist/identity/user-did-manager.js +69 -3
- package/dist/identity/user-did-manager.js.map +1 -1
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +43 -1
- package/dist/index.js.map +1 -1
- package/dist/runtime/audit-logger.d.ts +37 -0
- package/dist/runtime/audit-logger.d.ts.map +1 -0
- package/dist/runtime/audit-logger.js +9 -0
- package/dist/runtime/audit-logger.js.map +1 -0
- package/dist/runtime/base.d.ts +19 -2
- package/dist/runtime/base.d.ts.map +1 -1
- package/dist/runtime/base.js +227 -11
- package/dist/runtime/base.js.map +1 -1
- package/dist/services/access-control.service.d.ts.map +1 -1
- package/dist/services/access-control.service.js +199 -15
- package/dist/services/access-control.service.js.map +1 -1
- package/dist/services/authorization/authorization-registry.d.ts +29 -0
- package/dist/services/authorization/authorization-registry.d.ts.map +1 -0
- package/dist/services/authorization/authorization-registry.js +57 -0
- package/dist/services/authorization/authorization-registry.js.map +1 -0
- package/dist/services/authorization/types.d.ts +53 -0
- package/dist/services/authorization/types.d.ts.map +1 -0
- package/dist/services/authorization/types.js +10 -0
- package/dist/services/authorization/types.js.map +1 -0
- package/dist/services/batch-delegation.service.d.ts +53 -0
- package/dist/services/batch-delegation.service.d.ts.map +1 -0
- package/dist/services/batch-delegation.service.js +95 -0
- package/dist/services/batch-delegation.service.js.map +1 -0
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +4 -1
- package/dist/services/index.js.map +1 -1
- package/dist/services/oauth-config.service.d.ts +53 -0
- package/dist/services/oauth-config.service.d.ts.map +1 -0
- package/dist/services/oauth-config.service.js +141 -0
- package/dist/services/oauth-config.service.js.map +1 -0
- package/dist/services/oauth-provider-registry.d.ts +88 -0
- package/dist/services/oauth-provider-registry.d.ts.map +1 -0
- package/dist/services/oauth-provider-registry.js +128 -0
- package/dist/services/oauth-provider-registry.js.map +1 -0
- package/dist/services/oauth-service.d.ts +77 -0
- package/dist/services/oauth-service.d.ts.map +1 -0
- package/dist/services/oauth-service.js +373 -0
- package/dist/services/oauth-service.js.map +1 -0
- package/dist/services/oauth-token-retrieval.service.d.ts +49 -0
- package/dist/services/oauth-token-retrieval.service.d.ts.map +1 -0
- package/dist/services/oauth-token-retrieval.service.js +150 -0
- package/dist/services/oauth-token-retrieval.service.js.map +1 -0
- package/dist/services/provider-resolver.d.ts +48 -0
- package/dist/services/provider-resolver.d.ts.map +1 -0
- package/dist/services/provider-resolver.js +121 -0
- package/dist/services/provider-resolver.js.map +1 -0
- package/dist/services/provider-validator.d.ts +55 -0
- package/dist/services/provider-validator.d.ts.map +1 -0
- package/dist/services/provider-validator.js +135 -0
- package/dist/services/provider-validator.js.map +1 -0
- package/dist/services/session-registration.service.d.ts +80 -0
- package/dist/services/session-registration.service.d.ts.map +1 -0
- package/dist/services/session-registration.service.js +228 -0
- package/dist/services/session-registration.service.js.map +1 -0
- package/dist/services/tool-context-builder.d.ts +57 -0
- package/dist/services/tool-context-builder.d.ts.map +1 -0
- package/dist/services/tool-context-builder.js +125 -0
- package/dist/services/tool-context-builder.js.map +1 -0
- package/dist/services/tool-protection.service.d.ts +27 -0
- package/dist/services/tool-protection.service.d.ts.map +1 -1
- package/dist/services/tool-protection.service.js +194 -4
- package/dist/services/tool-protection.service.js.map +1 -1
- package/dist/types/oauth-required-error.d.ts +40 -0
- package/dist/types/oauth-required-error.d.ts.map +1 -0
- package/dist/types/oauth-required-error.js +40 -0
- package/dist/types/oauth-required-error.js.map +1 -0
- package/dist/utils/did-helpers.d.ts +33 -0
- package/dist/utils/did-helpers.d.ts.map +1 -1
- package/dist/utils/did-helpers.js +40 -0
- package/dist/utils/did-helpers.js.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/docs/API_REFERENCE.md +1362 -0
- package/docs/COMPLIANCE_MATRIX.md +691 -0
- package/docs/STATUSLIST2021_GUIDE.md +696 -0
- package/docs/W3C_VC_DELEGATION_GUIDE.md +710 -0
- package/package.json +23 -54
- package/scripts/audit-compliance.ts +724 -0
- package/src/__tests__/cache/tool-protection-cache.test.ts +640 -0
- package/src/__tests__/config/provider-runtime-config.test.ts +309 -0
- package/src/__tests__/delegation-e2e.test.ts +690 -0
- package/src/__tests__/identity/user-did-manager.test.ts +213 -0
- package/src/__tests__/index.test.ts +56 -0
- package/src/__tests__/integration/full-flow.test.ts +776 -0
- package/src/__tests__/integration.test.ts +281 -0
- package/src/__tests__/providers/base.test.ts +173 -0
- package/src/__tests__/providers/memory.test.ts +319 -0
- package/src/__tests__/regression/phase2-regression.test.ts +429 -0
- package/src/__tests__/runtime/audit-logger.test.ts +154 -0
- package/src/__tests__/runtime/base-extensions.test.ts +593 -0
- package/src/__tests__/runtime/base.test.ts +869 -0
- package/src/__tests__/runtime/delegation-flow.test.ts +164 -0
- package/src/__tests__/runtime/proof-client-did.test.ts +375 -0
- package/src/__tests__/runtime/route-interception.test.ts +686 -0
- package/src/__tests__/runtime/tool-protection-enforcement.test.ts +908 -0
- package/src/__tests__/services/agentshield-integration.test.ts +784 -0
- package/src/__tests__/services/cache-busting.test.ts +125 -0
- package/src/__tests__/services/oauth-service-pkce.test.ts +556 -0
- package/src/__tests__/services/provider-resolver-edge-cases.test.ts +591 -0
- package/src/__tests__/services/tool-protection-oauth-provider.test.ts +480 -0
- package/src/__tests__/services/tool-protection.service.test.ts +1366 -0
- package/src/__tests__/utils/mock-providers.ts +340 -0
- package/src/cache/oauth-config-cache.d.ts +69 -0
- package/src/cache/oauth-config-cache.d.ts.map +1 -0
- package/src/cache/oauth-config-cache.js.map +1 -0
- package/src/cache/oauth-config-cache.ts +123 -0
- package/src/cache/tool-protection-cache.ts +171 -0
- package/src/compliance/EXAMPLE.md +412 -0
- package/src/compliance/__tests__/schema-verifier.test.ts +797 -0
- package/src/compliance/index.ts +8 -0
- package/src/compliance/schema-registry.ts +460 -0
- package/src/compliance/schema-verifier.ts +708 -0
- package/src/config/__tests__/remote-config.spec.ts +268 -0
- package/src/config/remote-config.ts +174 -0
- package/src/config.ts +309 -0
- package/src/delegation/__tests__/audience-validator.test.ts +112 -0
- package/src/delegation/__tests__/bitstring.test.ts +346 -0
- package/src/delegation/__tests__/cascading-revocation.test.ts +628 -0
- package/src/delegation/__tests__/delegation-graph.test.ts +584 -0
- package/src/delegation/__tests__/utils.test.ts +152 -0
- package/src/delegation/__tests__/vc-issuer.test.ts +442 -0
- package/src/delegation/__tests__/vc-verifier.test.ts +922 -0
- package/src/delegation/audience-validator.ts +52 -0
- package/src/delegation/bitstring.ts +278 -0
- package/src/delegation/cascading-revocation.ts +370 -0
- package/src/delegation/delegation-graph.ts +299 -0
- package/src/delegation/index.ts +14 -0
- package/src/delegation/statuslist-manager.ts +353 -0
- package/src/delegation/storage/__tests__/memory-graph-storage.test.ts +366 -0
- package/src/delegation/storage/__tests__/memory-statuslist-storage.test.ts +228 -0
- package/src/delegation/storage/index.ts +9 -0
- package/src/delegation/storage/memory-graph-storage.ts +178 -0
- package/src/delegation/storage/memory-statuslist-storage.ts +77 -0
- package/src/delegation/utils.ts +42 -0
- package/src/delegation/vc-issuer.ts +232 -0
- package/src/delegation/vc-verifier.ts +568 -0
- package/src/identity/idp-token-resolver.ts +147 -0
- package/src/identity/idp-token-storage.interface.ts +59 -0
- package/src/identity/user-did-manager.ts +370 -0
- package/src/index.ts +271 -0
- package/src/providers/base.d.ts +91 -0
- package/src/providers/base.d.ts.map +1 -0
- package/src/providers/base.js.map +1 -0
- package/src/providers/base.ts +96 -0
- package/src/providers/memory.ts +142 -0
- package/src/runtime/audit-logger.ts +39 -0
- package/src/runtime/base.ts +1329 -0
- package/src/services/__tests__/access-control.integration.test.ts +443 -0
- package/src/services/__tests__/access-control.proof-response-validation.test.ts +578 -0
- package/src/services/__tests__/access-control.service.test.ts +970 -0
- package/src/services/__tests__/batch-delegation.service.test.ts +351 -0
- package/src/services/__tests__/crypto.service.test.ts +531 -0
- package/src/services/__tests__/oauth-provider-registry.test.ts +142 -0
- package/src/services/__tests__/proof-verifier.integration.test.ts +485 -0
- package/src/services/__tests__/proof-verifier.test.ts +489 -0
- package/src/services/__tests__/provider-resolution.integration.test.ts +202 -0
- package/src/services/__tests__/provider-resolver.test.ts +213 -0
- package/src/services/__tests__/storage.service.test.ts +358 -0
- package/src/services/access-control.service.ts +990 -0
- package/src/services/authorization/authorization-registry.ts +66 -0
- package/src/services/authorization/types.ts +71 -0
- package/src/services/batch-delegation.service.ts +137 -0
- package/src/services/crypto.service.ts +302 -0
- package/src/services/errors.ts +76 -0
- package/src/services/index.ts +18 -0
- package/src/services/oauth-config.service.d.ts +53 -0
- package/src/services/oauth-config.service.d.ts.map +1 -0
- package/src/services/oauth-config.service.js.map +1 -0
- package/src/services/oauth-config.service.ts +192 -0
- package/src/services/oauth-provider-registry.d.ts +57 -0
- package/src/services/oauth-provider-registry.d.ts.map +1 -0
- package/src/services/oauth-provider-registry.js.map +1 -0
- package/src/services/oauth-provider-registry.ts +141 -0
- package/src/services/oauth-service.ts +544 -0
- package/src/services/oauth-token-retrieval.service.ts +245 -0
- package/src/services/proof-verifier.ts +478 -0
- package/src/services/provider-resolver.d.ts +48 -0
- package/src/services/provider-resolver.d.ts.map +1 -0
- package/src/services/provider-resolver.js.map +1 -0
- package/src/services/provider-resolver.ts +146 -0
- package/src/services/provider-validator.ts +170 -0
- package/src/services/session-registration.service.ts +317 -0
- package/src/services/storage.service.ts +566 -0
- package/src/services/tool-context-builder.ts +172 -0
- package/src/services/tool-protection.service.ts +982 -0
- package/src/types/oauth-required-error.ts +63 -0
- package/src/types/tool-protection.ts +155 -0
- package/src/utils/__tests__/did-helpers.test.ts +101 -0
- package/src/utils/base64.ts +148 -0
- package/src/utils/cors.ts +83 -0
- package/src/utils/did-helpers.ts +150 -0
- package/src/utils/index.ts +8 -0
- package/src/utils/storage-keys.ts +278 -0
- package/tsconfig.json +21 -0
- package/vitest.config.ts +56 -0
|
@@ -0,0 +1,568 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Delegation Credential Verifier (Platform-Agnostic)
|
|
3
|
+
*
|
|
4
|
+
* Progressive enhancement verification for W3C Delegation Credentials.
|
|
5
|
+
* Follows the Edge-Delegation-Verification.md pattern:
|
|
6
|
+
*
|
|
7
|
+
* Stage 1: Fast basic checks (no network, early rejection)
|
|
8
|
+
* Stage 2: Parallel advanced checks (signature, status)
|
|
9
|
+
* Stage 3: Combined results
|
|
10
|
+
*
|
|
11
|
+
* Related Spec: MCP-I §4.3, W3C VC Data Model 1.1
|
|
12
|
+
* Python Reference: Edge-Delegation-Verification.md
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type {
|
|
16
|
+
DelegationCredential,
|
|
17
|
+
CredentialStatus,
|
|
18
|
+
} from '@kya-os/contracts';
|
|
19
|
+
import {
|
|
20
|
+
isDelegationCredentialExpired,
|
|
21
|
+
isDelegationCredentialNotYetValid,
|
|
22
|
+
validateDelegationCredential,
|
|
23
|
+
} from '@kya-os/contracts';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Verification result for delegation credentials
|
|
27
|
+
*/
|
|
28
|
+
export interface DelegationVCVerificationResult {
|
|
29
|
+
/** Whether the delegation credential is valid */
|
|
30
|
+
valid: boolean;
|
|
31
|
+
|
|
32
|
+
/** Reason for invalid result (if valid=false) */
|
|
33
|
+
reason?: string;
|
|
34
|
+
|
|
35
|
+
/** Stage at which verification completed */
|
|
36
|
+
stage: 'basic' | 'signature' | 'status' | 'complete';
|
|
37
|
+
|
|
38
|
+
/** Whether result came from cache */
|
|
39
|
+
cached?: boolean;
|
|
40
|
+
|
|
41
|
+
/** Performance metrics */
|
|
42
|
+
metrics?: {
|
|
43
|
+
basicCheckMs?: number;
|
|
44
|
+
signatureCheckMs?: number;
|
|
45
|
+
statusCheckMs?: number;
|
|
46
|
+
totalMs: number;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/** Details about what was checked */
|
|
50
|
+
checks?: {
|
|
51
|
+
basicValid?: boolean;
|
|
52
|
+
signatureValid?: boolean;
|
|
53
|
+
statusValid?: boolean;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Options for verification
|
|
59
|
+
*/
|
|
60
|
+
export interface VerifyDelegationVCOptions {
|
|
61
|
+
/** Skip cache and force fresh verification */
|
|
62
|
+
skipCache?: boolean;
|
|
63
|
+
|
|
64
|
+
/** Skip signature verification (faster, less secure) */
|
|
65
|
+
skipSignature?: boolean;
|
|
66
|
+
|
|
67
|
+
/** Skip status checking (faster, may miss revocations) */
|
|
68
|
+
skipStatus?: boolean;
|
|
69
|
+
|
|
70
|
+
/** DID resolver for fetching public keys */
|
|
71
|
+
didResolver?: DIDResolver;
|
|
72
|
+
|
|
73
|
+
/** Status list resolver for checking revocation */
|
|
74
|
+
statusListResolver?: StatusListResolver;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* DID Resolver interface
|
|
79
|
+
*/
|
|
80
|
+
export interface DIDResolver {
|
|
81
|
+
/**
|
|
82
|
+
* Resolve a DID to get the DID Document
|
|
83
|
+
* @param did - The DID to resolve
|
|
84
|
+
* @returns DID Document with verification methods
|
|
85
|
+
*/
|
|
86
|
+
resolve(did: string): Promise<DIDDocument | null>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* DID Document (simplified)
|
|
91
|
+
*/
|
|
92
|
+
export interface DIDDocument {
|
|
93
|
+
id: string;
|
|
94
|
+
verificationMethod?: VerificationMethod[];
|
|
95
|
+
authentication?: (string | VerificationMethod)[];
|
|
96
|
+
assertionMethod?: (string | VerificationMethod)[];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Verification Method
|
|
101
|
+
*/
|
|
102
|
+
export interface VerificationMethod {
|
|
103
|
+
id: string;
|
|
104
|
+
type: string;
|
|
105
|
+
controller: string;
|
|
106
|
+
publicKeyJwk?: any; // JWK type
|
|
107
|
+
publicKeyBase58?: string;
|
|
108
|
+
publicKeyMultibase?: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Status List Resolver interface
|
|
113
|
+
*/
|
|
114
|
+
export interface StatusListResolver {
|
|
115
|
+
/**
|
|
116
|
+
* Check if a credential is revoked via StatusList2021
|
|
117
|
+
* @param status - The credential status entry
|
|
118
|
+
* @returns true if revoked, false otherwise
|
|
119
|
+
*/
|
|
120
|
+
checkStatus(status: CredentialStatus): Promise<boolean>;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Signature verification function interface
|
|
125
|
+
*
|
|
126
|
+
* Platform-specific implementations provide this function.
|
|
127
|
+
*/
|
|
128
|
+
export interface SignatureVerificationFunction {
|
|
129
|
+
/**
|
|
130
|
+
* Verify an Ed25519 signature on a VC
|
|
131
|
+
*
|
|
132
|
+
* @param vc - The VC to verify
|
|
133
|
+
* @param publicKeyJwk - The public key in JWK format
|
|
134
|
+
* @returns Verification result
|
|
135
|
+
*/
|
|
136
|
+
(vc: DelegationCredential, publicKeyJwk: any): Promise<{
|
|
137
|
+
valid: boolean;
|
|
138
|
+
reason?: string;
|
|
139
|
+
}>;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Delegation Credential Verifier (Platform-Agnostic)
|
|
144
|
+
*
|
|
145
|
+
* Implements progressive enhancement pattern from Edge-Delegation-Verification.md:
|
|
146
|
+
* 1. Fast basic checks (no network) - early rejection
|
|
147
|
+
* 2. Parallel advanced checks (signature + status)
|
|
148
|
+
* 3. Combined results
|
|
149
|
+
*/
|
|
150
|
+
export class DelegationCredentialVerifier {
|
|
151
|
+
private didResolver?: DIDResolver;
|
|
152
|
+
private statusListResolver?: StatusListResolver;
|
|
153
|
+
private signatureVerifier?: SignatureVerificationFunction;
|
|
154
|
+
private cache = new Map<
|
|
155
|
+
string,
|
|
156
|
+
{ result: DelegationVCVerificationResult; expiresAt: number }
|
|
157
|
+
>();
|
|
158
|
+
private cacheTtl: number;
|
|
159
|
+
|
|
160
|
+
constructor(options?: {
|
|
161
|
+
didResolver?: DIDResolver;
|
|
162
|
+
statusListResolver?: StatusListResolver;
|
|
163
|
+
signatureVerifier?: SignatureVerificationFunction;
|
|
164
|
+
cacheTtl?: number;
|
|
165
|
+
}) {
|
|
166
|
+
this.didResolver = options?.didResolver;
|
|
167
|
+
this.statusListResolver = options?.statusListResolver;
|
|
168
|
+
this.signatureVerifier = options?.signatureVerifier;
|
|
169
|
+
this.cacheTtl = options?.cacheTtl || 60_000; // 1 minute default
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Verify a delegation credential with progressive enhancement
|
|
174
|
+
*
|
|
175
|
+
* Per Edge-Delegation-Verification.md:41-102
|
|
176
|
+
*
|
|
177
|
+
* @param vc - The delegation credential to verify
|
|
178
|
+
* @param options - Verification options
|
|
179
|
+
* @returns Verification result
|
|
180
|
+
*/
|
|
181
|
+
async verifyDelegationCredential(
|
|
182
|
+
vc: DelegationCredential,
|
|
183
|
+
options: VerifyDelegationVCOptions = {}
|
|
184
|
+
): Promise<DelegationVCVerificationResult> {
|
|
185
|
+
const startTime = Date.now();
|
|
186
|
+
|
|
187
|
+
// Check cache first
|
|
188
|
+
if (!options.skipCache) {
|
|
189
|
+
const cached = this.getFromCache(vc.id || '');
|
|
190
|
+
if (cached) {
|
|
191
|
+
return { ...cached, cached: true };
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ===================================================================
|
|
196
|
+
// STAGE 1: Fast Basic Checks (no network calls)
|
|
197
|
+
// Per Edge-Delegation-Verification.md:152-186
|
|
198
|
+
// ===================================================================
|
|
199
|
+
const basicCheckStart = Date.now();
|
|
200
|
+
const basicValidation = this.validateBasicProperties(vc);
|
|
201
|
+
const basicCheckMs = Date.now() - basicCheckStart;
|
|
202
|
+
|
|
203
|
+
if (!basicValidation.valid) {
|
|
204
|
+
const result: DelegationVCVerificationResult = {
|
|
205
|
+
valid: false,
|
|
206
|
+
reason: basicValidation.reason,
|
|
207
|
+
stage: 'basic',
|
|
208
|
+
metrics: {
|
|
209
|
+
basicCheckMs,
|
|
210
|
+
totalMs: Date.now() - startTime,
|
|
211
|
+
},
|
|
212
|
+
checks: {
|
|
213
|
+
basicValid: false,
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
return result;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// ===================================================================
|
|
220
|
+
// STAGE 2: Parallel Advanced Checks
|
|
221
|
+
// Per Edge-Delegation-Verification.md:281-301
|
|
222
|
+
// ===================================================================
|
|
223
|
+
|
|
224
|
+
// Start signature verification (if not skipped)
|
|
225
|
+
const signaturePromise = !options.skipSignature
|
|
226
|
+
? this.verifySignature(vc, options.didResolver || this.didResolver)
|
|
227
|
+
: Promise.resolve<{
|
|
228
|
+
valid: boolean;
|
|
229
|
+
reason?: string;
|
|
230
|
+
durationMs?: number;
|
|
231
|
+
}>({
|
|
232
|
+
valid: true,
|
|
233
|
+
durationMs: 0,
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// Start status checking (if not skipped)
|
|
237
|
+
const statusPromise =
|
|
238
|
+
!options.skipStatus && vc.credentialStatus
|
|
239
|
+
? this.checkCredentialStatus(
|
|
240
|
+
vc.credentialStatus,
|
|
241
|
+
options.statusListResolver || this.statusListResolver
|
|
242
|
+
)
|
|
243
|
+
: Promise.resolve<{
|
|
244
|
+
valid: boolean;
|
|
245
|
+
reason?: string;
|
|
246
|
+
durationMs?: number;
|
|
247
|
+
}>({
|
|
248
|
+
valid: true,
|
|
249
|
+
durationMs: 0,
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Wait for both checks in parallel
|
|
253
|
+
const [signatureResult, statusResult] = await Promise.all([
|
|
254
|
+
signaturePromise,
|
|
255
|
+
statusPromise,
|
|
256
|
+
]);
|
|
257
|
+
|
|
258
|
+
const signatureCheckMs = signatureResult.durationMs || 0;
|
|
259
|
+
const statusCheckMs = statusResult.durationMs || 0;
|
|
260
|
+
|
|
261
|
+
// ===================================================================
|
|
262
|
+
// STAGE 3: Combined Results
|
|
263
|
+
// Per Edge-Delegation-Verification.md:82-94
|
|
264
|
+
// ===================================================================
|
|
265
|
+
|
|
266
|
+
const allValid =
|
|
267
|
+
basicValidation.valid && signatureResult.valid && statusResult.valid;
|
|
268
|
+
|
|
269
|
+
const result: DelegationVCVerificationResult = {
|
|
270
|
+
valid: allValid,
|
|
271
|
+
reason: !allValid
|
|
272
|
+
? signatureResult.reason || statusResult.reason || 'Unknown failure'
|
|
273
|
+
: undefined,
|
|
274
|
+
stage: 'complete',
|
|
275
|
+
metrics: {
|
|
276
|
+
basicCheckMs,
|
|
277
|
+
signatureCheckMs,
|
|
278
|
+
statusCheckMs,
|
|
279
|
+
totalMs: Date.now() - startTime,
|
|
280
|
+
},
|
|
281
|
+
checks: {
|
|
282
|
+
basicValid: basicValidation.valid,
|
|
283
|
+
signatureValid: signatureResult.valid,
|
|
284
|
+
statusValid: statusResult.valid,
|
|
285
|
+
},
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// Cache successful verifications
|
|
289
|
+
if (result.valid && vc.id) {
|
|
290
|
+
this.setInCache(vc.id, result);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return result;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Stage 1: Validate basic properties (no network calls)
|
|
298
|
+
*
|
|
299
|
+
* Fast path for early rejection of invalid delegations.
|
|
300
|
+
* Per Edge-Delegation-Verification.md:155-186
|
|
301
|
+
*
|
|
302
|
+
* @param vc - The delegation credential
|
|
303
|
+
* @returns Validation result
|
|
304
|
+
*/
|
|
305
|
+
private validateBasicProperties(
|
|
306
|
+
vc: DelegationCredential
|
|
307
|
+
): { valid: boolean; reason?: string } {
|
|
308
|
+
// 1. Validate schema
|
|
309
|
+
const schemaValidation = validateDelegationCredential(vc);
|
|
310
|
+
if (!schemaValidation.success) {
|
|
311
|
+
return {
|
|
312
|
+
valid: false,
|
|
313
|
+
reason: `Schema validation failed: ${schemaValidation.error.message}`,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// 2. Check expiration
|
|
318
|
+
if (isDelegationCredentialExpired(vc)) {
|
|
319
|
+
return { valid: false, reason: 'Delegation credential expired' };
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// 3. Check not yet valid
|
|
323
|
+
if (isDelegationCredentialNotYetValid(vc)) {
|
|
324
|
+
return { valid: false, reason: 'Delegation credential not yet valid' };
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// 4. Check delegation status
|
|
328
|
+
const delegation = vc.credentialSubject.delegation;
|
|
329
|
+
if (delegation.status === 'revoked') {
|
|
330
|
+
return { valid: false, reason: 'Delegation status is revoked' };
|
|
331
|
+
}
|
|
332
|
+
if (delegation.status === 'expired') {
|
|
333
|
+
return { valid: false, reason: 'Delegation status is expired' };
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// 5. Check required fields
|
|
337
|
+
if (!delegation.issuerDid || !delegation.subjectDid) {
|
|
338
|
+
return { valid: false, reason: 'Missing issuer or subject DID' };
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// 6. Check proof exists (we'll verify it later)
|
|
342
|
+
if (!vc.proof) {
|
|
343
|
+
return { valid: false, reason: 'Missing proof' };
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return { valid: true };
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Stage 2a: Verify signature
|
|
351
|
+
*
|
|
352
|
+
* Per Edge-Delegation-Verification.md:191-234
|
|
353
|
+
*
|
|
354
|
+
* @param vc - The delegation credential
|
|
355
|
+
* @param didResolver - Optional DID resolver
|
|
356
|
+
* @returns Verification result
|
|
357
|
+
*/
|
|
358
|
+
private async verifySignature(
|
|
359
|
+
vc: DelegationCredential,
|
|
360
|
+
didResolver?: DIDResolver
|
|
361
|
+
): Promise<{ valid: boolean; reason?: string; durationMs?: number }> {
|
|
362
|
+
const startTime = Date.now();
|
|
363
|
+
|
|
364
|
+
try {
|
|
365
|
+
// Get issuer DID
|
|
366
|
+
const issuerDid =
|
|
367
|
+
typeof vc.issuer === 'string' ? vc.issuer : vc.issuer.id;
|
|
368
|
+
|
|
369
|
+
// If no DID resolver or signature verifier, skip verification
|
|
370
|
+
if (!didResolver || !this.signatureVerifier) {
|
|
371
|
+
return {
|
|
372
|
+
valid: true, // Trust but don't verify (no resolver/verifier available)
|
|
373
|
+
reason:
|
|
374
|
+
'No DID resolver or signature verifier available, skipping signature verification',
|
|
375
|
+
durationMs: Date.now() - startTime,
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// Resolve issuer DID to get public key
|
|
380
|
+
const didDoc = await didResolver.resolve(issuerDid);
|
|
381
|
+
if (!didDoc) {
|
|
382
|
+
return {
|
|
383
|
+
valid: false,
|
|
384
|
+
reason: `Could not resolve issuer DID: ${issuerDid}`,
|
|
385
|
+
durationMs: Date.now() - startTime,
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Find verification method from proof
|
|
390
|
+
if (!vc.proof) {
|
|
391
|
+
return {
|
|
392
|
+
valid: false,
|
|
393
|
+
reason: 'Proof is missing',
|
|
394
|
+
durationMs: Date.now() - startTime,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const verificationMethodId = vc.proof.verificationMethod;
|
|
399
|
+
if (!verificationMethodId) {
|
|
400
|
+
return {
|
|
401
|
+
valid: false,
|
|
402
|
+
reason: 'Proof missing verificationMethod',
|
|
403
|
+
durationMs: Date.now() - startTime,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const verificationMethod = this.findVerificationMethod(
|
|
408
|
+
didDoc,
|
|
409
|
+
verificationMethodId
|
|
410
|
+
);
|
|
411
|
+
if (!verificationMethod) {
|
|
412
|
+
return {
|
|
413
|
+
valid: false,
|
|
414
|
+
reason: `Verification method ${verificationMethodId} not found`,
|
|
415
|
+
durationMs: Date.now() - startTime,
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Extract public key
|
|
420
|
+
const publicKeyJwk = verificationMethod.publicKeyJwk;
|
|
421
|
+
if (!publicKeyJwk) {
|
|
422
|
+
return {
|
|
423
|
+
valid: false,
|
|
424
|
+
reason: 'Verification method missing publicKeyJwk',
|
|
425
|
+
durationMs: Date.now() - startTime,
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Verify signature using platform-specific verifier
|
|
430
|
+
const verificationResult = await this.signatureVerifier(vc, publicKeyJwk);
|
|
431
|
+
|
|
432
|
+
return {
|
|
433
|
+
valid: verificationResult.valid,
|
|
434
|
+
reason: verificationResult.reason,
|
|
435
|
+
durationMs: Date.now() - startTime,
|
|
436
|
+
};
|
|
437
|
+
} catch (error) {
|
|
438
|
+
return {
|
|
439
|
+
valid: false,
|
|
440
|
+
reason: `Signature verification error: ${
|
|
441
|
+
error instanceof Error ? error.message : 'Unknown error'
|
|
442
|
+
}`,
|
|
443
|
+
durationMs: Date.now() - startTime,
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Stage 2b: Check credential status via StatusList2021
|
|
450
|
+
*
|
|
451
|
+
* @param status - The credential status entry
|
|
452
|
+
* @param statusListResolver - Optional status list resolver
|
|
453
|
+
* @returns Status check result
|
|
454
|
+
*/
|
|
455
|
+
private async checkCredentialStatus(
|
|
456
|
+
status: CredentialStatus,
|
|
457
|
+
statusListResolver?: StatusListResolver
|
|
458
|
+
): Promise<{ valid: boolean; reason?: string; durationMs?: number }> {
|
|
459
|
+
const startTime = Date.now();
|
|
460
|
+
|
|
461
|
+
try {
|
|
462
|
+
// If no status list resolver, we can't check the status
|
|
463
|
+
if (!statusListResolver) {
|
|
464
|
+
return {
|
|
465
|
+
valid: true, // Trust but don't verify (no resolver available)
|
|
466
|
+
reason: 'No status list resolver available, skipping status check',
|
|
467
|
+
durationMs: Date.now() - startTime,
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// Check if credential is revoked/suspended
|
|
472
|
+
const isRevoked = await statusListResolver.checkStatus(status);
|
|
473
|
+
|
|
474
|
+
if (isRevoked) {
|
|
475
|
+
return {
|
|
476
|
+
valid: false,
|
|
477
|
+
reason: `Credential revoked via StatusList2021 (${status.statusPurpose})`,
|
|
478
|
+
durationMs: Date.now() - startTime,
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return {
|
|
483
|
+
valid: true,
|
|
484
|
+
durationMs: Date.now() - startTime,
|
|
485
|
+
};
|
|
486
|
+
} catch (error) {
|
|
487
|
+
return {
|
|
488
|
+
valid: false,
|
|
489
|
+
reason: `Status check error: ${
|
|
490
|
+
error instanceof Error ? error.message : 'Unknown error'
|
|
491
|
+
}`,
|
|
492
|
+
durationMs: Date.now() - startTime,
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* Find verification method in DID document
|
|
499
|
+
*
|
|
500
|
+
* @param didDoc - The DID document
|
|
501
|
+
* @param verificationMethodId - The verification method ID
|
|
502
|
+
* @returns Verification method or undefined
|
|
503
|
+
*/
|
|
504
|
+
private findVerificationMethod(
|
|
505
|
+
didDoc: DIDDocument,
|
|
506
|
+
verificationMethodId: string
|
|
507
|
+
): VerificationMethod | undefined {
|
|
508
|
+
return didDoc.verificationMethod?.find(
|
|
509
|
+
(vm) => vm.id === verificationMethodId
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Get from cache
|
|
515
|
+
*/
|
|
516
|
+
private getFromCache(id: string): DelegationVCVerificationResult | null {
|
|
517
|
+
const entry = this.cache.get(id);
|
|
518
|
+
if (!entry) return null;
|
|
519
|
+
|
|
520
|
+
if (Date.now() > entry.expiresAt) {
|
|
521
|
+
this.cache.delete(id);
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
return entry.result;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Set in cache
|
|
530
|
+
*/
|
|
531
|
+
private setInCache(id: string, result: DelegationVCVerificationResult): void {
|
|
532
|
+
this.cache.set(id, {
|
|
533
|
+
result,
|
|
534
|
+
expiresAt: Date.now() + this.cacheTtl,
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Clear cache
|
|
540
|
+
*/
|
|
541
|
+
clearCache(): void {
|
|
542
|
+
this.cache.clear();
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Clear cache entry for specific VC
|
|
547
|
+
*/
|
|
548
|
+
clearCacheEntry(id: string): void {
|
|
549
|
+
this.cache.delete(id);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* Create a delegation credential verifier
|
|
555
|
+
*
|
|
556
|
+
* Convenience factory function.
|
|
557
|
+
*
|
|
558
|
+
* @param options - Verifier options
|
|
559
|
+
* @returns DelegationCredentialVerifier instance
|
|
560
|
+
*/
|
|
561
|
+
export function createDelegationVerifier(options?: {
|
|
562
|
+
didResolver?: DIDResolver;
|
|
563
|
+
statusListResolver?: StatusListResolver;
|
|
564
|
+
signatureVerifier?: SignatureVerificationFunction;
|
|
565
|
+
cacheTtl?: number;
|
|
566
|
+
}): DelegationCredentialVerifier {
|
|
567
|
+
return new DelegationCredentialVerifier(options);
|
|
568
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IDP Token Resolver
|
|
3
|
+
*
|
|
4
|
+
* Resolves User DID to IDP access token (MH-7 requirement).
|
|
5
|
+
* Handles token lookup, expiration checking, and automatic refresh.
|
|
6
|
+
*
|
|
7
|
+
* @package @kya-os/mcp-i-core
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { IdpTokens } from "@kya-os/contracts/config";
|
|
11
|
+
import type { IIdpTokenStorage } from "./idp-token-storage.interface.js";
|
|
12
|
+
|
|
13
|
+
export interface IdpTokenResolverConfig {
|
|
14
|
+
/** Token storage implementation */
|
|
15
|
+
tokenStorage: IIdpTokenStorage;
|
|
16
|
+
|
|
17
|
+
/** OAuth service for token refresh */
|
|
18
|
+
oauthService: {
|
|
19
|
+
refreshToken(provider: string, refreshToken: string): Promise<IdpTokens | null>;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** Optional logger callback for diagnostics */
|
|
23
|
+
logger?: (message: string, data?: unknown) => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Service for resolving User DID to IDP access token
|
|
28
|
+
*
|
|
29
|
+
* MH-7 Requirement: resolveTokenFromDid(userDid: string): Promise<string>
|
|
30
|
+
*
|
|
31
|
+
* This service implements the core MH-7 functionality:
|
|
32
|
+
* - Resolves User DID to IDP access token
|
|
33
|
+
* - Handles token expiration and automatic refresh
|
|
34
|
+
* - Supports multiple IDP providers
|
|
35
|
+
*/
|
|
36
|
+
export class IdpTokenResolver {
|
|
37
|
+
private config: Required<Omit<IdpTokenResolverConfig, "logger">> & {
|
|
38
|
+
logger: (message: string, data?: unknown) => void;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
constructor(config: IdpTokenResolverConfig) {
|
|
42
|
+
this.config = {
|
|
43
|
+
tokenStorage: config.tokenStorage,
|
|
44
|
+
oauthService: config.oauthService,
|
|
45
|
+
logger: config.logger || (() => {}),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Resolve User DID to IDP access token
|
|
51
|
+
*
|
|
52
|
+
* MH-7 Requirement: resolveTokenFromDid(userDid: string): Promise<string>
|
|
53
|
+
*
|
|
54
|
+
* Flow:
|
|
55
|
+
* 1. Look up token from storage
|
|
56
|
+
* 2. Check expiration
|
|
57
|
+
* 3. Auto-refresh if expired and refresh_token available
|
|
58
|
+
* 4. Update storage after refresh
|
|
59
|
+
* 5. Return access_token or null
|
|
60
|
+
*
|
|
61
|
+
* @param userDid - User DID to resolve
|
|
62
|
+
* @param provider - OAuth provider name (e.g., "github", "google")
|
|
63
|
+
* @param scopes - Required scopes for token
|
|
64
|
+
* @returns Access token or null if not found/expired
|
|
65
|
+
*/
|
|
66
|
+
async resolveTokenFromDid(
|
|
67
|
+
userDid: string,
|
|
68
|
+
provider: string,
|
|
69
|
+
scopes: string[]
|
|
70
|
+
): Promise<string | null> {
|
|
71
|
+
// 1. Look up token from storage
|
|
72
|
+
const storedToken = await this.config.tokenStorage.getToken(
|
|
73
|
+
userDid,
|
|
74
|
+
provider,
|
|
75
|
+
scopes
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
if (!storedToken) {
|
|
79
|
+
this.config.logger("[IdpTokenResolver] Token not found", {
|
|
80
|
+
userDid: userDid.substring(0, 20) + "...",
|
|
81
|
+
provider,
|
|
82
|
+
scopes,
|
|
83
|
+
});
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 2. Check expiration
|
|
88
|
+
const now = Date.now();
|
|
89
|
+
if (storedToken.expires_at < now) {
|
|
90
|
+
this.config.logger("[IdpTokenResolver] Token expired, attempting refresh", {
|
|
91
|
+
userDid: userDid.substring(0, 20) + "...",
|
|
92
|
+
provider,
|
|
93
|
+
expiresAt: new Date(storedToken.expires_at).toISOString(),
|
|
94
|
+
hasRefreshToken: !!storedToken.refresh_token,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// 3. Refresh if refresh_token available
|
|
98
|
+
if (storedToken.refresh_token) {
|
|
99
|
+
const refreshed = await this.config.oauthService.refreshToken(
|
|
100
|
+
provider,
|
|
101
|
+
storedToken.refresh_token
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
if (refreshed) {
|
|
105
|
+
// 4. Update storage with new tokens
|
|
106
|
+
await this.config.tokenStorage.storeToken(
|
|
107
|
+
userDid,
|
|
108
|
+
provider,
|
|
109
|
+
scopes,
|
|
110
|
+
refreshed
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
this.config.logger("[IdpTokenResolver] Token refreshed successfully", {
|
|
114
|
+
userDid: userDid.substring(0, 20) + "...",
|
|
115
|
+
provider,
|
|
116
|
+
expiresAt: new Date(refreshed.expires_at).toISOString(),
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// 5. Return new access_token
|
|
120
|
+
return refreshed.access_token;
|
|
121
|
+
} else {
|
|
122
|
+
this.config.logger("[IdpTokenResolver] Token refresh failed", {
|
|
123
|
+
userDid: userDid.substring(0, 20) + "...",
|
|
124
|
+
provider,
|
|
125
|
+
});
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
this.config.logger("[IdpTokenResolver] Token expired and no refresh token", {
|
|
130
|
+
userDid: userDid.substring(0, 20) + "...",
|
|
131
|
+
provider,
|
|
132
|
+
});
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// 4. Return valid access_token
|
|
138
|
+
this.config.logger("[IdpTokenResolver] Token resolved successfully", {
|
|
139
|
+
userDid: userDid.substring(0, 20) + "...",
|
|
140
|
+
provider,
|
|
141
|
+
expiresAt: new Date(storedToken.expires_at).toISOString(),
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
return storedToken.access_token;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|