@kya-os/mcp-i-core 1.3.23 → 1.3.25
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/dist/identity/user-did-manager.d.ts +1 -1
- package/dist/identity/user-did-manager.js +32 -24
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -2
- package/dist/runtime/base.js +43 -29
- package/dist/services/provider-resolver.d.ts +18 -0
- package/dist/services/provider-resolver.js +36 -2
- package/dist/services/tool-protection.service.d.ts +25 -3
- package/dist/services/tool-protection.service.js +161 -249
- package/dist/types/tool-protection.d.ts +27 -4
- package/dist/utils/base64.js +48 -21
- package/dist/utils/storage-keys.js +6 -6
- package/package.json +2 -2
|
@@ -56,7 +56,7 @@ class UserDidManager {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
catch (error) {
|
|
59
|
-
console.warn(
|
|
59
|
+
console.warn("[UserDidManager] OAuth key pair lookup failed:", error);
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
// Check session storage if available
|
|
@@ -69,7 +69,7 @@ class UserDidManager {
|
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
catch (error) {
|
|
72
|
-
console.warn(
|
|
72
|
+
console.warn("[UserDidManager] Session key pair lookup failed:", error);
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
return null;
|
|
@@ -96,13 +96,16 @@ class UserDidManager {
|
|
|
96
96
|
return this.sessionDidCache.get(sessionId);
|
|
97
97
|
}
|
|
98
98
|
// PRIORITY 1: If OAuth identity provided, check for persistent user DID mapping
|
|
99
|
-
if (oauthIdentity &&
|
|
99
|
+
if (oauthIdentity &&
|
|
100
|
+
oauthIdentity.provider &&
|
|
101
|
+
oauthIdentity.subject &&
|
|
102
|
+
this.config.storage?.getByOAuth) {
|
|
100
103
|
try {
|
|
101
104
|
const persistentUserDid = await this.config.storage.getByOAuth(oauthIdentity.provider, oauthIdentity.subject);
|
|
102
105
|
if (persistentUserDid) {
|
|
103
|
-
console.
|
|
106
|
+
console.error("[UserDidManager] Found persistent user DID from OAuth mapping:", {
|
|
104
107
|
provider: oauthIdentity.provider,
|
|
105
|
-
userDid: persistentUserDid.substring(0, 20) +
|
|
108
|
+
userDid: persistentUserDid.substring(0, 20) + "...",
|
|
106
109
|
});
|
|
107
110
|
// Cache it for this session
|
|
108
111
|
this.sessionDidCache.set(sessionId, persistentUserDid);
|
|
@@ -113,7 +116,7 @@ class UserDidManager {
|
|
|
113
116
|
}
|
|
114
117
|
catch (error) {
|
|
115
118
|
// Log but continue - DID is cached and will be returned
|
|
116
|
-
console.warn(
|
|
119
|
+
console.warn("[UserDidManager] Failed to cache persistent DID in session storage:", error);
|
|
117
120
|
}
|
|
118
121
|
}
|
|
119
122
|
return persistentUserDid;
|
|
@@ -121,7 +124,7 @@ class UserDidManager {
|
|
|
121
124
|
}
|
|
122
125
|
catch (error) {
|
|
123
126
|
// Log but continue - will check session storage or generate new DID
|
|
124
|
-
console.warn(
|
|
127
|
+
console.warn("[UserDidManager] OAuth lookup failed, falling back to session storage:", error);
|
|
125
128
|
}
|
|
126
129
|
}
|
|
127
130
|
// PRIORITY 2: Check session storage if available
|
|
@@ -131,18 +134,21 @@ class UserDidManager {
|
|
|
131
134
|
if (storedDid) {
|
|
132
135
|
this.sessionDidCache.set(sessionId, storedDid);
|
|
133
136
|
// If OAuth identity provided but no persistent mapping found, create one now
|
|
134
|
-
if (oauthIdentity &&
|
|
137
|
+
if (oauthIdentity &&
|
|
138
|
+
oauthIdentity.provider &&
|
|
139
|
+
oauthIdentity.subject &&
|
|
140
|
+
this.config.storage.setByOAuth) {
|
|
135
141
|
try {
|
|
136
142
|
await this.config.storage.setByOAuth(oauthIdentity.provider, oauthIdentity.subject, storedDid, 90 * 24 * 60 * 60 // 90 days TTL for persistent mapping
|
|
137
143
|
);
|
|
138
|
-
console.
|
|
144
|
+
console.error("[UserDidManager] Created persistent OAuth mapping for existing user DID:", {
|
|
139
145
|
provider: oauthIdentity.provider,
|
|
140
|
-
userDid: storedDid.substring(0, 20) +
|
|
146
|
+
userDid: storedDid.substring(0, 20) + "...",
|
|
141
147
|
});
|
|
142
148
|
}
|
|
143
149
|
catch (error) {
|
|
144
150
|
// Log but continue - mapping creation failed, but DID is still valid
|
|
145
|
-
console.warn(
|
|
151
|
+
console.warn("[UserDidManager] Failed to create OAuth mapping:", error);
|
|
146
152
|
}
|
|
147
153
|
}
|
|
148
154
|
return storedDid;
|
|
@@ -150,7 +156,7 @@ class UserDidManager {
|
|
|
150
156
|
}
|
|
151
157
|
catch (error) {
|
|
152
158
|
// Log but continue - session will be anonymous
|
|
153
|
-
console.warn(
|
|
159
|
+
console.warn("[UserDidManager] Storage.get failed:", error);
|
|
154
160
|
}
|
|
155
161
|
}
|
|
156
162
|
// PHASE 5: No ephemeral DID generation - session stays anonymous
|
|
@@ -176,21 +182,23 @@ class UserDidManager {
|
|
|
176
182
|
await this.config.storage.set(sessionId, userDid, 1800); // 30 minutes TTL
|
|
177
183
|
}
|
|
178
184
|
catch (error) {
|
|
179
|
-
console.warn(
|
|
185
|
+
console.warn("[UserDidManager] Failed to store user DID in session storage:", error);
|
|
180
186
|
}
|
|
181
187
|
}
|
|
182
188
|
// Create OAuth mapping if provided
|
|
183
|
-
if (oauthIdentity?.provider &&
|
|
189
|
+
if (oauthIdentity?.provider &&
|
|
190
|
+
oauthIdentity?.subject &&
|
|
191
|
+
this.config.storage?.setByOAuth) {
|
|
184
192
|
try {
|
|
185
193
|
await this.config.storage.setByOAuth(oauthIdentity.provider, oauthIdentity.subject, userDid, 90 * 24 * 60 * 60 // 90 days TTL for persistent mapping
|
|
186
194
|
);
|
|
187
|
-
console.
|
|
195
|
+
console.error("[UserDidManager] Created OAuth → DID mapping:", {
|
|
188
196
|
provider: oauthIdentity.provider,
|
|
189
|
-
userDid: userDid.substring(0, 20) +
|
|
197
|
+
userDid: userDid.substring(0, 20) + "...",
|
|
190
198
|
});
|
|
191
199
|
}
|
|
192
200
|
catch (error) {
|
|
193
|
-
console.warn(
|
|
201
|
+
console.warn("[UserDidManager] Failed to create OAuth mapping:", error);
|
|
194
202
|
}
|
|
195
203
|
}
|
|
196
204
|
}
|
|
@@ -217,7 +225,7 @@ class UserDidManager {
|
|
|
217
225
|
// Generate did:web (requires web server setup)
|
|
218
226
|
// For now, fall back to did:key
|
|
219
227
|
// TODO: Implement did:web generation if needed
|
|
220
|
-
console.warn(
|
|
228
|
+
console.warn("[UserDidManager] did:web not yet implemented, using did:key");
|
|
221
229
|
}
|
|
222
230
|
// Generate Ed25519 keypair for user DID
|
|
223
231
|
const keyPair = await this.config.crypto.generateKeyPair();
|
|
@@ -258,21 +266,21 @@ class UserDidManager {
|
|
|
258
266
|
* Simple implementation for did:key generation
|
|
259
267
|
*/
|
|
260
268
|
base58Encode(bytes) {
|
|
261
|
-
const alphabet =
|
|
269
|
+
const alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
262
270
|
let num = BigInt(0);
|
|
263
271
|
// Convert bytes to big integer
|
|
264
272
|
for (let i = 0; i < bytes.length; i++) {
|
|
265
273
|
num = num * BigInt(256) + BigInt(bytes[i]);
|
|
266
274
|
}
|
|
267
275
|
// Convert to base58
|
|
268
|
-
let result =
|
|
276
|
+
let result = "";
|
|
269
277
|
while (num > 0) {
|
|
270
278
|
result = alphabet[Number(num % BigInt(58))] + result;
|
|
271
279
|
num = num / BigInt(58);
|
|
272
280
|
}
|
|
273
281
|
// Add leading zeros
|
|
274
282
|
for (let i = 0; i < bytes.length && bytes[i] === 0; i++) {
|
|
275
|
-
result =
|
|
283
|
+
result = "1" + result;
|
|
276
284
|
}
|
|
277
285
|
return result;
|
|
278
286
|
}
|
|
@@ -280,9 +288,9 @@ class UserDidManager {
|
|
|
280
288
|
* Convert base64 string to Uint8Array
|
|
281
289
|
*/
|
|
282
290
|
base64ToBytes(base64) {
|
|
283
|
-
if (typeof Buffer !==
|
|
291
|
+
if (typeof Buffer !== "undefined") {
|
|
284
292
|
// Node.js environment
|
|
285
|
-
return new Uint8Array(Buffer.from(base64,
|
|
293
|
+
return new Uint8Array(Buffer.from(base64, "base64"));
|
|
286
294
|
}
|
|
287
295
|
else {
|
|
288
296
|
// Browser/Workers environment
|
|
@@ -323,7 +331,7 @@ class UserDidManager {
|
|
|
323
331
|
}
|
|
324
332
|
catch (error) {
|
|
325
333
|
// Log but continue - cache is already cleared
|
|
326
|
-
console.warn(
|
|
334
|
+
console.warn("[UserDidManager] Storage.delete failed, continuing:", error);
|
|
327
335
|
}
|
|
328
336
|
}
|
|
329
337
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -26,8 +26,8 @@ export type { OAuthServiceConfig } from "./services/oauth-service";
|
|
|
26
26
|
export { ToolContextBuilder } from "./services/tool-context-builder";
|
|
27
27
|
export type { ToolContextBuilderConfig } from "./services/tool-context-builder";
|
|
28
28
|
export { OAuthProviderRegistry } from "./services/oauth-provider-registry";
|
|
29
|
-
export { ProviderResolver } from "./services/provider-resolver";
|
|
30
|
-
export { ProviderValidator, ProviderValidationError } from "./services/provider-validator";
|
|
29
|
+
export { ProviderResolver, ConsentOnlyModeError, } from "./services/provider-resolver";
|
|
30
|
+
export { ProviderValidator, ProviderValidationError, } from "./services/provider-validator";
|
|
31
31
|
export { OAuthTokenRetrievalService } from "./services/oauth-token-retrieval.service";
|
|
32
32
|
export type { OAuthTokenRetrievalServiceConfig } from "./services/oauth-token-retrieval.service";
|
|
33
33
|
export { BatchDelegationService } from "./services/batch-delegation.service";
|
|
@@ -52,7 +52,7 @@ export { CascadingRevocationManager, createCascadingRevocationManager, type Revo
|
|
|
52
52
|
export { MemoryStatusListStorage } from "./delegation/storage/memory-statuslist-storage";
|
|
53
53
|
export { MemoryDelegationGraphStorage } from "./delegation/storage/memory-graph-storage";
|
|
54
54
|
export { createDidKeyResolver, isEd25519DidKey, extractPublicKeyFromDidKey, publicKeyToJwk, resolveDidKeySync, } from "./delegation/did-key-resolver";
|
|
55
|
-
export { base58Encode, base58Decode, isValidBase58
|
|
55
|
+
export { base58Encode, base58Decode, isValidBase58 } from "./utils/base58";
|
|
56
56
|
export { SchemaVerifier, createSchemaVerifier, type SchemaMetadata, type FieldComplianceResult, type SchemaComplianceReport, type FullComplianceReport, } from "./compliance/schema-verifier";
|
|
57
57
|
export { SCHEMA_REGISTRY, getAllSchemas, getSchemasByCategory, getSchemaById, getCriticalSchemas, getSchemaStats, } from "./compliance/schema-registry";
|
|
58
58
|
export { canonicalizeJSON, createUnsignedVCJWT, completeVCJWT, parseVCJWT, type VCJWTHeader, type VCJWTPayload, type EncodeVCAsJWTOptions, } from "./delegation/utils";
|
package/dist/index.js
CHANGED
|
@@ -20,8 +20,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
20
20
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
21
21
|
};
|
|
22
22
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
-
exports.
|
|
24
|
-
exports.IdpTokenResolver = exports.UserDidManager = exports.fetchRemoteConfig = exports.bytesToBase64 = exports.base64urlDecodeToString = exports.base64urlDecodeToBytes = exports.base64urlEncodeFromString = exports.base64urlEncodeFromBytes = exports.parseVCJWT = exports.completeVCJWT = exports.createUnsignedVCJWT = exports.canonicalizeJSON = exports.getSchemaStats = exports.getCriticalSchemas = exports.getSchemaById = exports.getSchemasByCategory = exports.getAllSchemas = exports.SCHEMA_REGISTRY = exports.createSchemaVerifier = exports.SchemaVerifier = exports.isValidBase58 = exports.base58Decode = exports.base58Encode = exports.resolveDidKeySync = exports.publicKeyToJwk = exports.extractPublicKeyFromDidKey = exports.isEd25519DidKey = exports.createDidKeyResolver = exports.MemoryDelegationGraphStorage = exports.MemoryStatusListStorage = void 0;
|
|
23
|
+
exports.CascadingRevocationManager = exports.createDelegationGraph = exports.DelegationGraphManager = exports.isIndexSet = exports.BitstringManager = exports.createStatusListManager = exports.StatusList2021Manager = exports.createDelegationVerifier = exports.DelegationCredentialVerifier = exports.createDelegationIssuer = exports.DelegationCredentialIssuer = exports.OAuthRequiredError = exports.DelegationRequiredError = exports.NoOpToolProtectionCache = exports.InMemoryToolProtectionCache = exports.createProofVerificationError = exports.PROOF_VERIFICATION_ERROR_CODES = exports.ProofVerificationError = exports.migrateLegacyKeys = exports.StorageKeyHelpers = exports.createStorageProviders = exports.NoOpOAuthConfigCache = exports.InMemoryOAuthConfigCache = exports.BatchDelegationService = exports.OAuthTokenRetrievalService = exports.ProviderValidationError = exports.ProviderValidator = exports.ConsentOnlyModeError = exports.ProviderResolver = exports.OAuthProviderRegistry = exports.ToolContextBuilder = exports.OAuthService = exports.OAuthConfigService = exports.createSessionRegistrationService = exports.SessionRegistrationService = exports.authorizationMatches = exports.AccessControlApiService = exports.ProofVerifier = exports.CryptoService = exports.ToolProtectionService = exports.MCPIRuntimeBase = exports.MemoryIdentityProvider = exports.MemoryNonceCacheProvider = exports.MemoryStorageProvider = exports.IdentityProvider = exports.NonceCacheProvider = exports.StorageProvider = exports.FetchProvider = exports.ClockProvider = exports.CryptoProvider = void 0;
|
|
24
|
+
exports.IdpTokenResolver = exports.UserDidManager = exports.fetchRemoteConfig = exports.bytesToBase64 = exports.base64urlDecodeToString = exports.base64urlDecodeToBytes = exports.base64urlEncodeFromString = exports.base64urlEncodeFromBytes = exports.parseVCJWT = exports.completeVCJWT = exports.createUnsignedVCJWT = exports.canonicalizeJSON = exports.getSchemaStats = exports.getCriticalSchemas = exports.getSchemaById = exports.getSchemasByCategory = exports.getAllSchemas = exports.SCHEMA_REGISTRY = exports.createSchemaVerifier = exports.SchemaVerifier = exports.isValidBase58 = exports.base58Decode = exports.base58Encode = exports.resolveDidKeySync = exports.publicKeyToJwk = exports.extractPublicKeyFromDidKey = exports.isEd25519DidKey = exports.createDidKeyResolver = exports.MemoryDelegationGraphStorage = exports.MemoryStatusListStorage = exports.createCascadingRevocationManager = void 0;
|
|
25
25
|
// Base providers
|
|
26
26
|
var base_1 = require("./providers/base");
|
|
27
27
|
Object.defineProperty(exports, "CryptoProvider", { enumerable: true, get: function () { return base_1.CryptoProvider; } });
|
|
@@ -72,6 +72,7 @@ Object.defineProperty(exports, "OAuthProviderRegistry", { enumerable: true, get:
|
|
|
72
72
|
// Provider Resolver (Phase 2)
|
|
73
73
|
var provider_resolver_1 = require("./services/provider-resolver");
|
|
74
74
|
Object.defineProperty(exports, "ProviderResolver", { enumerable: true, get: function () { return provider_resolver_1.ProviderResolver; } });
|
|
75
|
+
Object.defineProperty(exports, "ConsentOnlyModeError", { enumerable: true, get: function () { return provider_resolver_1.ConsentOnlyModeError; } });
|
|
75
76
|
// Provider Validator (Phase 3)
|
|
76
77
|
var provider_validator_1 = require("./services/provider-validator");
|
|
77
78
|
Object.defineProperty(exports, "ProviderValidator", { enumerable: true, get: function () { return provider_validator_1.ProviderValidator; } });
|
package/dist/runtime/base.js
CHANGED
|
@@ -113,14 +113,14 @@ class MCPIRuntimeBase {
|
|
|
113
113
|
userDid = resolvedDid ?? undefined;
|
|
114
114
|
if (this.config.audit?.enabled) {
|
|
115
115
|
if (userDid) {
|
|
116
|
-
console.
|
|
116
|
+
console.error("[MCP-I] Resolved existing user DID for session:", {
|
|
117
117
|
userDid: userDid.substring(0, 20) + "...",
|
|
118
118
|
hasOAuth: !!oauthIdentity,
|
|
119
119
|
provider: oauthIdentity?.provider,
|
|
120
120
|
});
|
|
121
121
|
}
|
|
122
122
|
else {
|
|
123
|
-
console.
|
|
123
|
+
console.error("[MCP-I] Session started anonymous (no userDid):", {
|
|
124
124
|
sessionId: sessionId.substring(0, 8) + "...",
|
|
125
125
|
hasOAuth: !!oauthIdentity,
|
|
126
126
|
});
|
|
@@ -219,7 +219,7 @@ class MCPIRuntimeBase {
|
|
|
219
219
|
await this.userDidManager.setUserDidForSession(sessionId, userDid, oauthIdentity);
|
|
220
220
|
}
|
|
221
221
|
if (this.config.audit?.enabled) {
|
|
222
|
-
console.
|
|
222
|
+
console.error("[MCP-I] Session identity updated (Phase 5):", {
|
|
223
223
|
sessionId: sessionId.substring(0, 8) + "...",
|
|
224
224
|
userDid: userDid.substring(0, 20) + "...",
|
|
225
225
|
provider: oauthIdentity?.provider,
|
|
@@ -248,7 +248,7 @@ class MCPIRuntimeBase {
|
|
|
248
248
|
// Get agent identity to check protection
|
|
249
249
|
const identity = await this.getIdentity();
|
|
250
250
|
if (this.config.audit?.enabled) {
|
|
251
|
-
console.
|
|
251
|
+
console.error("[MCP-I] Checking tool protection:", {
|
|
252
252
|
tool: toolName,
|
|
253
253
|
agentDid: identity.did.slice(0, 20) + "...",
|
|
254
254
|
hasDelegation: !!(session?.delegationToken || session?.consentProof),
|
|
@@ -312,7 +312,7 @@ class MCPIRuntimeBase {
|
|
|
312
312
|
// Verify delegation token with AccessControlApiService
|
|
313
313
|
try {
|
|
314
314
|
if (this.config.audit?.enabled) {
|
|
315
|
-
console.
|
|
315
|
+
console.error("[MCP-I] 🔐 Verifying delegation token with AccessControlApiService", {
|
|
316
316
|
tool: toolName,
|
|
317
317
|
agentDid: identity.did.slice(0, 20) + "...",
|
|
318
318
|
hasDelegationToken: !!delegationToken,
|
|
@@ -331,7 +331,7 @@ class MCPIRuntimeBase {
|
|
|
331
331
|
if (session?.userDid) {
|
|
332
332
|
verifyRequest.user_did = session.userDid;
|
|
333
333
|
if (this.config.audit?.enabled) {
|
|
334
|
-
console.
|
|
334
|
+
console.error("[MCP-I] 🔐 Including user_did in verification request", {
|
|
335
335
|
userDid: session.userDid.slice(0, 20) + "...",
|
|
336
336
|
});
|
|
337
337
|
}
|
|
@@ -442,7 +442,7 @@ class MCPIRuntimeBase {
|
|
|
442
442
|
}
|
|
443
443
|
// User identifier validation passed (direct match or trusted AgentShield verification)
|
|
444
444
|
if (this.config.audit?.enabled) {
|
|
445
|
-
console.
|
|
445
|
+
console.error("[MCP-I] ✅ User identifier validation PASSED", {
|
|
446
446
|
tool: toolName,
|
|
447
447
|
agentDid: identity.did.slice(0, 20) + "...",
|
|
448
448
|
userDid: sessionUserDid.substring(0, 20) + "...",
|
|
@@ -469,12 +469,39 @@ class MCPIRuntimeBase {
|
|
|
469
469
|
}
|
|
470
470
|
// ✅ SECURITY: Validate authorization method matches tool requirements
|
|
471
471
|
// This prevents stale delegations from working after tool auth method changes
|
|
472
|
-
//
|
|
473
|
-
//
|
|
474
|
-
// 2. Credential contains authorization data (AgentShield API verifier)
|
|
472
|
+
// NOTE: Authorization validation only applies when credential is present (AgentShield API verifier).
|
|
473
|
+
// KV/Memory verifiers don't return credential data, so we skip auth method validation for them.
|
|
475
474
|
const toolAuth = protection.authorization;
|
|
476
475
|
const delegationAuth = credential?.authorization;
|
|
477
|
-
|
|
476
|
+
// Only validate when:
|
|
477
|
+
// 1. Tool has authorization requirement defined
|
|
478
|
+
// 2. Credential is present (AgentShield API verifier)
|
|
479
|
+
if (toolAuth && credential) {
|
|
480
|
+
// If tool requires authorization but credential doesn't have auth data, reject
|
|
481
|
+
// This prevents bypassing auth checks with incomplete credential data
|
|
482
|
+
if (!delegationAuth) {
|
|
483
|
+
if (this.config.audit?.enabled) {
|
|
484
|
+
console.error("[MCP-I] ❌ Tool requires authorization but credential missing auth data", {
|
|
485
|
+
tool: toolName,
|
|
486
|
+
agentDid: identity.did.slice(0, 20) + "...",
|
|
487
|
+
toolAuth,
|
|
488
|
+
note: "Credential exists but missing authorization metadata - rejecting for security",
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
// Throw DelegationRequiredError to force re-authorization
|
|
492
|
+
const interceptedCall = {
|
|
493
|
+
toolName,
|
|
494
|
+
args,
|
|
495
|
+
sessionId: session?.id || "unknown",
|
|
496
|
+
timestamp: this.clock.now(),
|
|
497
|
+
expiresAt: this.clock.calculateExpiry(1800),
|
|
498
|
+
};
|
|
499
|
+
const resumeToken = this.generateResumeToken(interceptedCall);
|
|
500
|
+
const consentUrl = this.buildConsentUrl(toolName, protection.requiredScopes, session, resumeToken, undefined, protection.oauthProvider);
|
|
501
|
+
this.interceptedCalls.set(resumeToken, interceptedCall);
|
|
502
|
+
this.cleanupExpiredInterceptedCalls();
|
|
503
|
+
throw new tool_protection_js_1.DelegationRequiredError(toolName, protection.requiredScopes, consentUrl, interceptedCall, resumeToken);
|
|
504
|
+
}
|
|
478
505
|
// Both tool and delegation have authorization - compare them
|
|
479
506
|
if (!(0, access_control_service_js_1.authorizationMatches)(delegationAuth, toolAuth)) {
|
|
480
507
|
const authMismatchReason = `Authorization method mismatch: delegation has ${delegationAuth.type}${delegationAuth.provider ? `:${delegationAuth.provider}` : ""}${delegationAuth.credentialType ? `:${delegationAuth.credentialType}` : ""} but tool requires ${toolAuth.type}${toolAuth.provider ? `:${toolAuth.provider}` : ""}${toolAuth.credentialType ? `:${toolAuth.credentialType}` : ""}`;
|
|
@@ -503,7 +530,7 @@ class MCPIRuntimeBase {
|
|
|
503
530
|
}
|
|
504
531
|
// Authorization method validation passed
|
|
505
532
|
if (this.config.audit?.enabled) {
|
|
506
|
-
console.
|
|
533
|
+
console.error("[MCP-I] ✅ Authorization method validation PASSED", {
|
|
507
534
|
tool: toolName,
|
|
508
535
|
agentDid: identity.did.slice(0, 20) + "...",
|
|
509
536
|
authType: delegationAuth.type,
|
|
@@ -511,23 +538,10 @@ class MCPIRuntimeBase {
|
|
|
511
538
|
});
|
|
512
539
|
}
|
|
513
540
|
}
|
|
514
|
-
|
|
515
|
-
// Tool requires authorization but credential doesn't have auth data
|
|
516
|
-
// This can happen with legacy delegations or incomplete credential data
|
|
517
|
-
if (this.config.audit?.enabled) {
|
|
518
|
-
console.warn("[MCP-I] ⚠️ Tool requires authorization but credential missing auth data", {
|
|
519
|
-
tool: toolName,
|
|
520
|
-
agentDid: identity.did.slice(0, 20) + "...",
|
|
521
|
-
toolAuth,
|
|
522
|
-
note: "Allowing execution - legacy delegation or incomplete data",
|
|
523
|
-
});
|
|
524
|
-
}
|
|
525
|
-
// Allow execution for backward compatibility with legacy delegations
|
|
526
|
-
// New delegations from AgentShield will always have authorization data
|
|
527
|
-
}
|
|
541
|
+
// If credential is missing entirely (KV/Memory verifiers), skip validation for backward compatibility
|
|
528
542
|
// Verification succeeded
|
|
529
543
|
if (this.config.audit?.enabled) {
|
|
530
|
-
console.
|
|
544
|
+
console.error("[MCP-I] ✅ Delegation verification SUCCEEDED", {
|
|
531
545
|
tool: toolName,
|
|
532
546
|
agentDid: identity.did.slice(0, 20) + "...",
|
|
533
547
|
delegationId: verificationResult.data.delegation_id,
|
|
@@ -600,7 +614,7 @@ class MCPIRuntimeBase {
|
|
|
600
614
|
else {
|
|
601
615
|
// No protection required - tool can be executed freely
|
|
602
616
|
if (this.config.audit?.enabled) {
|
|
603
|
-
console.
|
|
617
|
+
console.error("[MCP-I] Tool protection check passed (no delegation required)", {
|
|
604
618
|
tool: toolName,
|
|
605
619
|
agentDid: identity.did.slice(0, 20) + "...",
|
|
606
620
|
reason: "Tool not configured to require delegation",
|
|
@@ -1106,7 +1120,7 @@ class MCPIRuntimeBase {
|
|
|
1106
1120
|
this.config.audit.logFunction(logLine);
|
|
1107
1121
|
}
|
|
1108
1122
|
else {
|
|
1109
|
-
console.
|
|
1123
|
+
console.error("[AUDIT]", logLine);
|
|
1110
1124
|
}
|
|
1111
1125
|
}
|
|
1112
1126
|
createDIDDocument(identity) {
|
|
@@ -9,10 +9,19 @@
|
|
|
9
9
|
import type { ToolProtection } from "@kya-os/contracts/tool-protection";
|
|
10
10
|
import type { OAuthProviderRegistry } from "./oauth-provider-registry.js";
|
|
11
11
|
import type { OAuthConfigService } from "./oauth-config.service.js";
|
|
12
|
+
/**
|
|
13
|
+
* Error thrown when a tool is configured for consent-only mode (no OAuth needed)
|
|
14
|
+
* This is NOT an error condition - it's a signal to skip OAuth flow
|
|
15
|
+
*/
|
|
16
|
+
export declare class ConsentOnlyModeError extends Error {
|
|
17
|
+
readonly toolProtection: ToolProtection;
|
|
18
|
+
constructor(toolProtection: ToolProtection);
|
|
19
|
+
}
|
|
12
20
|
/**
|
|
13
21
|
* Resolves OAuth provider for tools with priority-based fallback strategy
|
|
14
22
|
*
|
|
15
23
|
* Priority order:
|
|
24
|
+
* 0. Check for consent-only mode (authorization.type === 'none') - skip OAuth
|
|
16
25
|
* 1. Tool-specific oauthProvider field (Phase 2+ preferred)
|
|
17
26
|
* 2. Scope prefix inference (fallback)
|
|
18
27
|
* 3. Project-configured provider from AgentShield dashboard
|
|
@@ -22,12 +31,21 @@ export declare class ProviderResolver {
|
|
|
22
31
|
private registry;
|
|
23
32
|
private configService;
|
|
24
33
|
constructor(registry: OAuthProviderRegistry, configService: OAuthConfigService);
|
|
34
|
+
/**
|
|
35
|
+
* Check if a tool is configured for consent-only mode
|
|
36
|
+
* Consent-only means delegation is required but no OAuth provider is needed
|
|
37
|
+
*
|
|
38
|
+
* @param toolProtection - Tool protection configuration
|
|
39
|
+
* @returns true if consent-only mode
|
|
40
|
+
*/
|
|
41
|
+
isConsentOnlyMode(toolProtection: ToolProtection): boolean;
|
|
25
42
|
/**
|
|
26
43
|
* Resolve OAuth provider for a tool
|
|
27
44
|
*
|
|
28
45
|
* @param toolProtection - Tool protection configuration
|
|
29
46
|
* @param projectId - Project ID for fetching provider config
|
|
30
47
|
* @returns Provider name (never null - throws if cannot resolve)
|
|
48
|
+
* @throws ConsentOnlyModeError if tool is consent-only (not a real error)
|
|
31
49
|
* @throws Error if provider cannot be resolved
|
|
32
50
|
*/
|
|
33
51
|
resolveProvider(toolProtection: ToolProtection, projectId: string): Promise<string>;
|
|
@@ -8,11 +8,25 @@
|
|
|
8
8
|
* @package @kya-os/mcp-i-core
|
|
9
9
|
*/
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
-
exports.ProviderResolver = void 0;
|
|
11
|
+
exports.ProviderResolver = exports.ConsentOnlyModeError = void 0;
|
|
12
|
+
/**
|
|
13
|
+
* Error thrown when a tool is configured for consent-only mode (no OAuth needed)
|
|
14
|
+
* This is NOT an error condition - it's a signal to skip OAuth flow
|
|
15
|
+
*/
|
|
16
|
+
class ConsentOnlyModeError extends Error {
|
|
17
|
+
toolProtection;
|
|
18
|
+
constructor(toolProtection) {
|
|
19
|
+
super("Tool is configured for consent-only mode (no OAuth provider required)");
|
|
20
|
+
this.toolProtection = toolProtection;
|
|
21
|
+
this.name = "ConsentOnlyModeError";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.ConsentOnlyModeError = ConsentOnlyModeError;
|
|
12
25
|
/**
|
|
13
26
|
* Resolves OAuth provider for tools with priority-based fallback strategy
|
|
14
27
|
*
|
|
15
28
|
* Priority order:
|
|
29
|
+
* 0. Check for consent-only mode (authorization.type === 'none') - skip OAuth
|
|
16
30
|
* 1. Tool-specific oauthProvider field (Phase 2+ preferred)
|
|
17
31
|
* 2. Scope prefix inference (fallback)
|
|
18
32
|
* 3. Project-configured provider from AgentShield dashboard
|
|
@@ -25,15 +39,35 @@ class ProviderResolver {
|
|
|
25
39
|
this.registry = registry;
|
|
26
40
|
this.configService = configService;
|
|
27
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Check if a tool is configured for consent-only mode
|
|
44
|
+
* Consent-only means delegation is required but no OAuth provider is needed
|
|
45
|
+
*
|
|
46
|
+
* @param toolProtection - Tool protection configuration
|
|
47
|
+
* @returns true if consent-only mode
|
|
48
|
+
*/
|
|
49
|
+
isConsentOnlyMode(toolProtection) {
|
|
50
|
+
// Check explicit authorization.type === 'none'
|
|
51
|
+
if (toolProtection.authorization?.type === "none") {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
28
56
|
/**
|
|
29
57
|
* Resolve OAuth provider for a tool
|
|
30
58
|
*
|
|
31
59
|
* @param toolProtection - Tool protection configuration
|
|
32
60
|
* @param projectId - Project ID for fetching provider config
|
|
33
61
|
* @returns Provider name (never null - throws if cannot resolve)
|
|
62
|
+
* @throws ConsentOnlyModeError if tool is consent-only (not a real error)
|
|
34
63
|
* @throws Error if provider cannot be resolved
|
|
35
64
|
*/
|
|
36
65
|
async resolveProvider(toolProtection, projectId) {
|
|
66
|
+
// Priority 0: Check for consent-only mode (no OAuth required)
|
|
67
|
+
if (this.isConsentOnlyMode(toolProtection)) {
|
|
68
|
+
console.error(`[ProviderResolver] Tool is consent-only mode (authorization.type=none), skipping OAuth resolution`);
|
|
69
|
+
throw new ConsentOnlyModeError(toolProtection);
|
|
70
|
+
}
|
|
37
71
|
// Priority 1: Tool-specific provider (Phase 2+ preferred)
|
|
38
72
|
if (toolProtection.oauthProvider) {
|
|
39
73
|
// Ensure registry is loaded before checking
|
|
@@ -54,7 +88,7 @@ class ProviderResolver {
|
|
|
54
88
|
await this.registry.loadFromAgentShield(projectId);
|
|
55
89
|
}
|
|
56
90
|
if (this.registry.hasProvider(inferredProvider)) {
|
|
57
|
-
console.
|
|
91
|
+
console.error(`[ProviderResolver] Inferred provider "${inferredProvider}" from scopes`);
|
|
58
92
|
return inferredProvider;
|
|
59
93
|
}
|
|
60
94
|
}
|
|
@@ -4,11 +4,23 @@
|
|
|
4
4
|
* This service manages tool protection configuration from AgentShield API with
|
|
5
5
|
* efficient caching and automatic synchronization support.
|
|
6
6
|
*
|
|
7
|
+
* CONFIGURATION PRECEDENCE (highest to lowest):
|
|
8
|
+
* ---------------------------------------------
|
|
9
|
+
* 1. Remote config (AgentShield API) - WINS (source of truth)
|
|
10
|
+
* 2. Local config (localConfig) - Base defaults, overridden by remote
|
|
11
|
+
* 3. Framework defaults
|
|
12
|
+
*
|
|
13
|
+
* OFFLINE FALLBACK (only when API unavailable):
|
|
14
|
+
* ---------------------------------------------
|
|
15
|
+
* 1. Stale cache (if allowStaleCache=true and within maxStaleCacheAge)
|
|
16
|
+
* 2. offlineFallbackConfig (explicit offline-only config)
|
|
17
|
+
* 3. failSafeBehavior (deny-all or allow-all)
|
|
18
|
+
*
|
|
7
19
|
* CORE FUNCTIONALITY:
|
|
8
20
|
* -------------------
|
|
9
|
-
* 1. Fetches tool protection config from AgentShield API
|
|
10
|
-
* 2.
|
|
11
|
-
* 3.
|
|
21
|
+
* 1. Fetches tool protection config from AgentShield API (remote wins)
|
|
22
|
+
* 2. Merges with local config (local serves as base defaults)
|
|
23
|
+
* 3. Caches responses with configurable TTL (default 5 minutes)
|
|
12
24
|
* 4. Provides delegation requirement checking before tool execution
|
|
13
25
|
*
|
|
14
26
|
* SYNCHRONIZATION WITH AGENTSHIELD:
|
|
@@ -133,6 +145,16 @@ export declare class ToolProtectionService {
|
|
|
133
145
|
* @param options.bypassCDNCache When true, adds cache-busting to bypass CDN caches (used by clearAndRefresh)
|
|
134
146
|
*/
|
|
135
147
|
private fetchFromApi;
|
|
148
|
+
/**
|
|
149
|
+
* Parse tool protections from API response
|
|
150
|
+
*
|
|
151
|
+
* Extracts tool protection configs from merged config format:
|
|
152
|
+
* { data: { config: { toolProtection: { tools: {...} } } } }
|
|
153
|
+
*
|
|
154
|
+
* @param response API response from AgentShield
|
|
155
|
+
* @returns Record of tool name to ToolProtection
|
|
156
|
+
*/
|
|
157
|
+
private parseToolProtectionsFromResponse;
|
|
136
158
|
/**
|
|
137
159
|
* Clear the cache for a project or agent (useful for testing or manual refresh)
|
|
138
160
|
*
|