@kya-os/mcp-i-core 1.3.10-canary.clientinfo.20251126124133 → 1.3.10
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 +1 -1
- package/dist/__tests__/utils/mock-providers.d.ts +2 -1
- package/dist/__tests__/utils/mock-providers.d.ts.map +1 -1
- package/dist/__tests__/utils/mock-providers.js.map +1 -1
- package/dist/config/remote-config.d.ts +51 -0
- package/dist/config/remote-config.d.ts.map +1 -1
- package/dist/config/remote-config.js +74 -0
- package/dist/config/remote-config.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +4 -1
- package/dist/config.js.map +1 -1
- package/dist/delegation/did-key-resolver.d.ts +64 -0
- package/dist/delegation/did-key-resolver.d.ts.map +1 -0
- package/dist/delegation/did-key-resolver.js +159 -0
- package/dist/delegation/did-key-resolver.js.map +1 -0
- package/dist/delegation/utils.d.ts +76 -0
- package/dist/delegation/utils.d.ts.map +1 -1
- package/dist/delegation/utils.js +117 -0
- package/dist/delegation/utils.js.map +1 -1
- package/dist/identity/user-did-manager.d.ts +95 -12
- package/dist/identity/user-did-manager.d.ts.map +1 -1
- package/dist/identity/user-did-manager.js +107 -25
- package/dist/identity/user-did-manager.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -1
- package/dist/index.js.map +1 -1
- package/dist/runtime/base.d.ts +25 -8
- package/dist/runtime/base.d.ts.map +1 -1
- package/dist/runtime/base.js +74 -21
- package/dist/runtime/base.js.map +1 -1
- package/dist/services/session-registration.service.d.ts.map +1 -1
- package/dist/services/session-registration.service.js +10 -90
- package/dist/services/session-registration.service.js.map +1 -1
- package/dist/services/tool-protection.service.d.ts +5 -2
- package/dist/services/tool-protection.service.d.ts.map +1 -1
- package/dist/services/tool-protection.service.js +72 -24
- package/dist/services/tool-protection.service.js.map +1 -1
- package/dist/utils/base58.d.ts +31 -0
- package/dist/utils/base58.d.ts.map +1 -0
- package/dist/utils/base58.js +103 -0
- package/dist/utils/base58.js.map +1 -0
- package/package.json +3 -3
- package/src/__tests__/identity/user-did-manager.test.ts +64 -45
- package/src/__tests__/integration/full-flow.test.ts +23 -10
- package/src/__tests__/runtime/base-extensions.test.ts +23 -21
- package/src/__tests__/runtime/proof-client-did.test.ts +19 -18
- package/src/__tests__/services/agentshield-integration.test.ts +10 -3
- package/src/__tests__/services/tool-protection-merged-config.test.ts +485 -0
- package/src/__tests__/services/tool-protection.service.test.ts +18 -11
- package/src/config/__tests__/merged-config.spec.ts +445 -0
- package/src/config/remote-config.ts +90 -0
- package/src/config.ts +3 -0
- package/src/delegation/__tests__/did-key-resolver.test.ts +265 -0
- package/src/delegation/did-key-resolver.ts +179 -0
- package/src/delegation/utils.ts +179 -0
- package/src/identity/user-did-manager.ts +185 -29
- package/src/index.ts +36 -1
- package/src/runtime/base.ts +84 -21
- package/src/services/session-registration.service.ts +26 -121
- package/src/services/tool-protection.service.ts +125 -56
- package/src/utils/base58.ts +109 -0
- package/coverage/coverage-final.json +0 -57
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* User DID Manager
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* Generates did:key DIDs for users when they join a chat session.
|
|
4
|
+
* Manages user DIDs for MCP-I sessions.
|
|
6
5
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* Phase 5: Anonymous Sessions Until OAuth
|
|
7
|
+
* - Sessions start anonymous (no userDid) until OAuth completes
|
|
8
|
+
* - User DIDs are resolved via AgentShield identity/resolve after OAuth
|
|
9
|
+
* - Eliminates DID fragmentation (same user = same DID across sessions)
|
|
10
|
+
*
|
|
11
|
+
* DID Resolution Priority:
|
|
12
|
+
* 1. OAuth mapping lookup (persistent)
|
|
13
|
+
* 2. Session storage lookup
|
|
14
|
+
* 3. Return null (session stays anonymous)
|
|
9
15
|
*/
|
|
10
16
|
|
|
11
17
|
import { CryptoProvider } from '../providers/base';
|
|
@@ -35,6 +41,35 @@ export interface OAuthIdentity {
|
|
|
35
41
|
name?: string;
|
|
36
42
|
}
|
|
37
43
|
|
|
44
|
+
/**
|
|
45
|
+
* User key pair for signing VCs
|
|
46
|
+
*
|
|
47
|
+
* Contains both public and private keys in base64 format.
|
|
48
|
+
* SECURITY: Private keys should be encrypted at rest.
|
|
49
|
+
*/
|
|
50
|
+
export interface UserKeyPair {
|
|
51
|
+
/**
|
|
52
|
+
* User DID (did:key format)
|
|
53
|
+
*/
|
|
54
|
+
did: string;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Public key in base64 format
|
|
58
|
+
*/
|
|
59
|
+
publicKey: string;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Private key in base64 format
|
|
63
|
+
* SECURITY: Should be encrypted at rest in production
|
|
64
|
+
*/
|
|
65
|
+
privateKey: string;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Key ID (for JWS header)
|
|
69
|
+
*/
|
|
70
|
+
keyId: string;
|
|
71
|
+
}
|
|
72
|
+
|
|
38
73
|
/**
|
|
39
74
|
* User DID storage interface
|
|
40
75
|
*/
|
|
@@ -65,6 +100,35 @@ export interface UserDidStorage {
|
|
|
65
100
|
* If not implemented, OAuth-based storage will be skipped
|
|
66
101
|
*/
|
|
67
102
|
setByOAuth?(provider: string, subject: string, did: string, ttl?: number): Promise<void>;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Get user key pair for a session (optional - for VC signing)
|
|
106
|
+
* If not implemented, VC issuance will not be available for this session
|
|
107
|
+
*/
|
|
108
|
+
getKeyPair?(sessionId: string): Promise<UserKeyPair | null>;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Store user key pair for a session (optional - for VC signing)
|
|
112
|
+
* SECURITY: Implementation should encrypt private keys at rest
|
|
113
|
+
*/
|
|
114
|
+
setKeyPair?(sessionId: string, keyPair: UserKeyPair, ttl?: number): Promise<void>;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get user key pair by OAuth identity (optional - for persistent key storage)
|
|
118
|
+
* If not implemented, OAuth-based key lookup will be skipped
|
|
119
|
+
*/
|
|
120
|
+
getKeyPairByOAuth?(provider: string, subject: string): Promise<UserKeyPair | null>;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Store user key pair for OAuth identity (optional - for persistent key storage)
|
|
124
|
+
* SECURITY: Implementation should encrypt private keys at rest
|
|
125
|
+
*/
|
|
126
|
+
setKeyPairByOAuth?(
|
|
127
|
+
provider: string,
|
|
128
|
+
subject: string,
|
|
129
|
+
keyPair: UserKeyPair,
|
|
130
|
+
ttl?: number
|
|
131
|
+
): Promise<void>;
|
|
68
132
|
}
|
|
69
133
|
|
|
70
134
|
/**
|
|
@@ -102,28 +166,84 @@ export interface UserDidManagerConfig {
|
|
|
102
166
|
export class UserDidManager {
|
|
103
167
|
private config: UserDidManagerConfig;
|
|
104
168
|
private sessionDidCache = new Map<string, string>();
|
|
169
|
+
private sessionKeyPairCache = new Map<string, UserKeyPair>();
|
|
105
170
|
|
|
106
171
|
constructor(config: UserDidManagerConfig) {
|
|
107
172
|
this.config = config;
|
|
108
173
|
}
|
|
109
174
|
|
|
110
175
|
/**
|
|
111
|
-
*
|
|
176
|
+
* Get key pair for a session (for VC signing)
|
|
177
|
+
*
|
|
178
|
+
* Returns the key pair if available, null otherwise.
|
|
179
|
+
* Key pairs are stored when DIDs are generated.
|
|
180
|
+
*
|
|
181
|
+
* @param sessionId - MCP session ID
|
|
182
|
+
* @param oauthIdentity - Optional OAuth identity for persistent lookup
|
|
183
|
+
* @returns UserKeyPair or null if not available
|
|
184
|
+
*/
|
|
185
|
+
async getKeyPairForSession(
|
|
186
|
+
sessionId: string,
|
|
187
|
+
oauthIdentity?: OAuthIdentity | null
|
|
188
|
+
): Promise<UserKeyPair | null> {
|
|
189
|
+
// Check in-memory cache first
|
|
190
|
+
if (this.sessionKeyPairCache.has(sessionId)) {
|
|
191
|
+
return this.sessionKeyPairCache.get(sessionId)!;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Check OAuth-based persistent storage if available
|
|
195
|
+
if (
|
|
196
|
+
oauthIdentity?.provider &&
|
|
197
|
+
oauthIdentity?.subject &&
|
|
198
|
+
this.config.storage?.getKeyPairByOAuth
|
|
199
|
+
) {
|
|
200
|
+
try {
|
|
201
|
+
const keyPair = await this.config.storage.getKeyPairByOAuth(
|
|
202
|
+
oauthIdentity.provider,
|
|
203
|
+
oauthIdentity.subject
|
|
204
|
+
);
|
|
205
|
+
if (keyPair) {
|
|
206
|
+
this.sessionKeyPairCache.set(sessionId, keyPair);
|
|
207
|
+
return keyPair;
|
|
208
|
+
}
|
|
209
|
+
} catch (error) {
|
|
210
|
+
console.warn('[UserDidManager] OAuth key pair lookup failed:', error);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Check session storage if available
|
|
215
|
+
if (this.config.storage?.getKeyPair) {
|
|
216
|
+
try {
|
|
217
|
+
const keyPair = await this.config.storage.getKeyPair(sessionId);
|
|
218
|
+
if (keyPair) {
|
|
219
|
+
this.sessionKeyPairCache.set(sessionId, keyPair);
|
|
220
|
+
return keyPair;
|
|
221
|
+
}
|
|
222
|
+
} catch (error) {
|
|
223
|
+
console.warn('[UserDidManager] Session key pair lookup failed:', error);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Get user DID for a session (Phase 5: No ephemeral generation)
|
|
112
232
|
*
|
|
113
233
|
* If a user DID already exists for the session, it is returned.
|
|
114
|
-
* If OAuth identity is provided, checks for persistent user DID mapping
|
|
115
|
-
*
|
|
234
|
+
* If OAuth identity is provided, checks for persistent user DID mapping.
|
|
235
|
+
* Returns null if no DID found - session stays anonymous until OAuth completes.
|
|
116
236
|
*
|
|
117
237
|
* @param sessionId - MCP session ID
|
|
118
238
|
* @param oauthIdentity - Optional OAuth identity for persistent user DID lookup
|
|
119
|
-
* @returns User DID (did:key format)
|
|
239
|
+
* @returns User DID (did:key format) or null if session is anonymous
|
|
120
240
|
*
|
|
121
241
|
* @remarks
|
|
122
|
-
* -
|
|
123
|
-
* -
|
|
124
|
-
* -
|
|
242
|
+
* - Phase 5: Sessions start anonymous, no ephemeral DID generation
|
|
243
|
+
* - User DIDs are resolved via AgentShield after OAuth completes
|
|
244
|
+
* - Returns null if no existing DID found (instead of generating ephemeral)
|
|
125
245
|
*/
|
|
126
|
-
async getOrCreateUserDid(sessionId: string, oauthIdentity?: OAuthIdentity | null): Promise<string> {
|
|
246
|
+
async getOrCreateUserDid(sessionId: string, oauthIdentity?: OAuthIdentity | null): Promise<string | null> {
|
|
127
247
|
// Check cache first
|
|
128
248
|
if (this.sessionDidCache.has(sessionId)) {
|
|
129
249
|
return this.sessionDidCache.get(sessionId)!;
|
|
@@ -187,29 +307,45 @@ export class UserDidManager {
|
|
|
187
307
|
return storedDid;
|
|
188
308
|
}
|
|
189
309
|
} catch (error) {
|
|
190
|
-
// Log but continue - will
|
|
191
|
-
console.warn('[UserDidManager] Storage.get failed
|
|
310
|
+
// Log but continue - session will be anonymous
|
|
311
|
+
console.warn('[UserDidManager] Storage.get failed:', error);
|
|
192
312
|
}
|
|
193
313
|
}
|
|
194
314
|
|
|
195
|
-
//
|
|
196
|
-
|
|
315
|
+
// PHASE 5: No ephemeral DID generation - session stays anonymous
|
|
316
|
+
// User DID will be resolved via AgentShield after OAuth completes
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
197
319
|
|
|
198
|
-
|
|
320
|
+
/**
|
|
321
|
+
* Set user DID for a session (Phase 5: After OAuth resolution)
|
|
322
|
+
*
|
|
323
|
+
* Called after AgentShield identity/resolve returns a persistent user DID.
|
|
324
|
+
* Caches the DID and optionally stores in session storage.
|
|
325
|
+
*
|
|
326
|
+
* @param sessionId - MCP session ID
|
|
327
|
+
* @param userDid - Persistent user DID from AgentShield
|
|
328
|
+
* @param oauthIdentity - OAuth identity for creating persistent mappings
|
|
329
|
+
*/
|
|
330
|
+
async setUserDidForSession(
|
|
331
|
+
sessionId: string,
|
|
332
|
+
userDid: string,
|
|
333
|
+
oauthIdentity?: OAuthIdentity | null
|
|
334
|
+
): Promise<void> {
|
|
335
|
+
// Cache in memory
|
|
199
336
|
this.sessionDidCache.set(sessionId, userDid);
|
|
200
337
|
|
|
201
|
-
// Store
|
|
338
|
+
// Store in session storage if available
|
|
202
339
|
if (this.config.storage) {
|
|
203
340
|
try {
|
|
204
341
|
await this.config.storage.set(sessionId, userDid, 1800); // 30 minutes TTL
|
|
205
342
|
} catch (error) {
|
|
206
|
-
|
|
207
|
-
console.warn('[UserDidManager] Storage.set failed, continuing with cached DID:', error);
|
|
343
|
+
console.warn('[UserDidManager] Failed to store user DID in session storage:', error);
|
|
208
344
|
}
|
|
209
345
|
}
|
|
210
346
|
|
|
211
|
-
//
|
|
212
|
-
if (oauthIdentity
|
|
347
|
+
// Create OAuth mapping if provided
|
|
348
|
+
if (oauthIdentity?.provider && oauthIdentity?.subject && this.config.storage?.setByOAuth) {
|
|
213
349
|
try {
|
|
214
350
|
await this.config.storage.setByOAuth(
|
|
215
351
|
oauthIdentity.provider,
|
|
@@ -217,17 +353,14 @@ export class UserDidManager {
|
|
|
217
353
|
userDid,
|
|
218
354
|
90 * 24 * 60 * 60 // 90 days TTL for persistent mapping
|
|
219
355
|
);
|
|
220
|
-
console.log('[UserDidManager] Created
|
|
356
|
+
console.log('[UserDidManager] Created OAuth → DID mapping:', {
|
|
221
357
|
provider: oauthIdentity.provider,
|
|
222
358
|
userDid: userDid.substring(0, 20) + '...',
|
|
223
359
|
});
|
|
224
360
|
} catch (error) {
|
|
225
|
-
// Log but continue - mapping creation failed, but DID is still valid
|
|
226
361
|
console.warn('[UserDidManager] Failed to create OAuth mapping:', error);
|
|
227
362
|
}
|
|
228
363
|
}
|
|
229
|
-
|
|
230
|
-
return userDid;
|
|
231
364
|
}
|
|
232
365
|
|
|
233
366
|
/**
|
|
@@ -237,6 +370,19 @@ export class UserDidManager {
|
|
|
237
370
|
* did:web can be used if configured, but requires additional setup.
|
|
238
371
|
*/
|
|
239
372
|
private async generateUserDid(): Promise<string> {
|
|
373
|
+
const keyPairData = await this.generateUserDidWithKeyPair();
|
|
374
|
+
return keyPairData.did;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Generate a new ephemeral user DID with full key pair
|
|
379
|
+
*
|
|
380
|
+
* Returns the DID along with the key pair for VC signing.
|
|
381
|
+
* Uses did:key format by default.
|
|
382
|
+
*
|
|
383
|
+
* @returns UserKeyPair containing DID, public key, private key, and key ID
|
|
384
|
+
*/
|
|
385
|
+
private async generateUserDidWithKeyPair(): Promise<UserKeyPair> {
|
|
240
386
|
if (this.config.useDidWeb && this.config.didWebBaseUrl) {
|
|
241
387
|
// Generate did:web (requires web server setup)
|
|
242
388
|
// For now, fall back to did:key
|
|
@@ -246,12 +392,22 @@ export class UserDidManager {
|
|
|
246
392
|
|
|
247
393
|
// Generate Ed25519 keypair for user DID
|
|
248
394
|
const keyPair = await this.config.crypto.generateKeyPair();
|
|
249
|
-
|
|
395
|
+
|
|
250
396
|
// Extract public key bytes (32 bytes for Ed25519)
|
|
251
397
|
const publicKeyBytes = this.base64ToBytes(keyPair.publicKey);
|
|
252
|
-
|
|
398
|
+
|
|
253
399
|
// Generate did:key from public key
|
|
254
|
-
|
|
400
|
+
const did = this.generateDidKeyFromPublicKey(publicKeyBytes);
|
|
401
|
+
|
|
402
|
+
// Key ID is the DID with #keys-1 fragment (standard for did:key)
|
|
403
|
+
const keyId = `${did}#keys-1`;
|
|
404
|
+
|
|
405
|
+
return {
|
|
406
|
+
did,
|
|
407
|
+
publicKey: keyPair.publicKey,
|
|
408
|
+
privateKey: keyPair.privateKey,
|
|
409
|
+
keyId,
|
|
410
|
+
};
|
|
255
411
|
}
|
|
256
412
|
|
|
257
413
|
/**
|
package/src/index.ts
CHANGED
|
@@ -201,6 +201,22 @@ export { MemoryStatusListStorage } from "./delegation/storage/memory-statuslist-
|
|
|
201
201
|
|
|
202
202
|
export { MemoryDelegationGraphStorage } from "./delegation/storage/memory-graph-storage";
|
|
203
203
|
|
|
204
|
+
// DID:key Resolver (Phase 3 VC Verification)
|
|
205
|
+
export {
|
|
206
|
+
createDidKeyResolver,
|
|
207
|
+
isEd25519DidKey,
|
|
208
|
+
extractPublicKeyFromDidKey,
|
|
209
|
+
publicKeyToJwk,
|
|
210
|
+
resolveDidKeySync,
|
|
211
|
+
} from "./delegation/did-key-resolver";
|
|
212
|
+
|
|
213
|
+
// Base58 Utilities (for did:key encoding/decoding)
|
|
214
|
+
export {
|
|
215
|
+
base58Encode,
|
|
216
|
+
base58Decode,
|
|
217
|
+
isValidBase58,
|
|
218
|
+
} from "./utils/base58";
|
|
219
|
+
|
|
204
220
|
// Compliance Verification (with JSON Schema draft-07 support)
|
|
205
221
|
export {
|
|
206
222
|
SchemaVerifier,
|
|
@@ -220,7 +236,24 @@ export {
|
|
|
220
236
|
getSchemaStats,
|
|
221
237
|
} from "./compliance/schema-registry";
|
|
222
238
|
|
|
223
|
-
export {
|
|
239
|
+
export {
|
|
240
|
+
canonicalizeJSON,
|
|
241
|
+
createUnsignedVCJWT,
|
|
242
|
+
completeVCJWT,
|
|
243
|
+
parseVCJWT,
|
|
244
|
+
type VCJWTHeader,
|
|
245
|
+
type VCJWTPayload,
|
|
246
|
+
type EncodeVCAsJWTOptions,
|
|
247
|
+
} from "./delegation/utils";
|
|
248
|
+
|
|
249
|
+
// Base64 utilities for VC JWT encoding
|
|
250
|
+
export {
|
|
251
|
+
base64urlEncodeFromBytes,
|
|
252
|
+
base64urlEncodeFromString,
|
|
253
|
+
base64urlDecodeToBytes,
|
|
254
|
+
base64urlDecodeToString,
|
|
255
|
+
bytesToBase64,
|
|
256
|
+
} from "./utils/base64";
|
|
224
257
|
|
|
225
258
|
// Re-export commonly used types from contracts
|
|
226
259
|
// Note: @kya-os/contracts exports are at the root level
|
|
@@ -263,6 +296,8 @@ export { UserDidManager } from "./identity/user-did-manager";
|
|
|
263
296
|
export type {
|
|
264
297
|
UserDidStorage,
|
|
265
298
|
UserDidManagerConfig,
|
|
299
|
+
UserKeyPair,
|
|
300
|
+
OAuthIdentity,
|
|
266
301
|
} from "./identity/user-did-manager";
|
|
267
302
|
|
|
268
303
|
// IDP Token Resolver (Phase 1 - MH-7)
|
package/src/runtime/base.ts
CHANGED
|
@@ -130,19 +130,16 @@ export class MCPIRuntimeBase {
|
|
|
130
130
|
return this.cachedIdentity;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
-
/**
|
|
134
|
-
* Handle handshake request
|
|
135
|
-
*/
|
|
136
133
|
/**
|
|
137
134
|
* Handle MCP handshake request
|
|
138
135
|
*
|
|
136
|
+
* Phase 5: Anonymous Sessions Until OAuth
|
|
137
|
+
* - Sessions start anonymous (no userDid) unless OAuth identity provided
|
|
138
|
+
* - User DID is resolved via AgentShield after OAuth completes
|
|
139
|
+
* - Eliminates DID fragmentation (same user = same DID across sessions)
|
|
140
|
+
*
|
|
139
141
|
* @param request - Handshake request object (may include oauthIdentity for persistent user DID lookup)
|
|
140
142
|
* @returns Handshake response with session ID and agent DID
|
|
141
|
-
*
|
|
142
|
-
* @remarks
|
|
143
|
-
* - Accepts optional oauthIdentity via request.oauthIdentity (backward compatible)
|
|
144
|
-
* - If OAuth identity provided, uses it to retrieve/create persistent user DID
|
|
145
|
-
* - Falls back to ephemeral user DID generation if OAuth unavailable
|
|
146
143
|
*/
|
|
147
144
|
async handleHandshake(
|
|
148
145
|
request: any & {
|
|
@@ -155,26 +152,36 @@ export class MCPIRuntimeBase {
|
|
|
155
152
|
const timestamp = this.clock.now();
|
|
156
153
|
const sessionId = await this.generateSessionId();
|
|
157
154
|
|
|
158
|
-
//
|
|
159
|
-
//
|
|
155
|
+
// Phase 5: Try to resolve user DID from existing OAuth mapping
|
|
156
|
+
// Sessions start anonymous - no ephemeral generation
|
|
160
157
|
let userDid: string | undefined;
|
|
161
158
|
if (this.userDidManager) {
|
|
162
159
|
try {
|
|
163
160
|
const oauthIdentity = request.oauthIdentity;
|
|
164
|
-
|
|
161
|
+
const resolvedDid = await this.userDidManager.getOrCreateUserDid(
|
|
165
162
|
sessionId,
|
|
166
163
|
oauthIdentity
|
|
167
164
|
);
|
|
165
|
+
// Convert null to undefined for session storage
|
|
166
|
+
userDid = resolvedDid ?? undefined;
|
|
167
|
+
|
|
168
168
|
if (this.config.audit?.enabled) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
169
|
+
if (userDid) {
|
|
170
|
+
console.log("[MCP-I] Resolved existing user DID for session:", {
|
|
171
|
+
userDid: userDid.substring(0, 20) + "...",
|
|
172
|
+
hasOAuth: !!oauthIdentity,
|
|
173
|
+
provider: oauthIdentity?.provider,
|
|
174
|
+
});
|
|
175
|
+
} else {
|
|
176
|
+
console.log("[MCP-I] Session started anonymous (no userDid):", {
|
|
177
|
+
sessionId: sessionId.substring(0, 8) + "...",
|
|
178
|
+
hasOAuth: !!oauthIdentity,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
174
181
|
}
|
|
175
182
|
} catch (error) {
|
|
176
|
-
console.warn("[MCP-I] Failed to
|
|
177
|
-
// Continue without user DID -
|
|
183
|
+
console.warn("[MCP-I] Failed to resolve user DID:", error);
|
|
184
|
+
// Continue without user DID - session is anonymous
|
|
178
185
|
}
|
|
179
186
|
}
|
|
180
187
|
|
|
@@ -211,11 +218,11 @@ export class MCPIRuntimeBase {
|
|
|
211
218
|
}
|
|
212
219
|
: undefined;
|
|
213
220
|
|
|
214
|
-
// Create session
|
|
221
|
+
// Create session with Phase 5 identity state
|
|
215
222
|
const session = {
|
|
216
223
|
id: sessionId,
|
|
217
224
|
clientDid: request.clientDid || userDid, // Use provided clientDid or generated userDid
|
|
218
|
-
userDid: userDid, // Store
|
|
225
|
+
userDid: userDid, // Store user DID (may be undefined for anonymous sessions)
|
|
219
226
|
agentDid: request.agentDid, // ✅ FIXED: Only agent DID, no fallback
|
|
220
227
|
serverDid: identity.did, // ✅ NEW: Server's DID (for clarity)
|
|
221
228
|
audience: request.audience,
|
|
@@ -223,7 +230,10 @@ export class MCPIRuntimeBase {
|
|
|
223
230
|
expiresAt: this.clock.calculateExpiry(
|
|
224
231
|
(this.config.session?.ttlMinutes || 30) * 60
|
|
225
232
|
),
|
|
226
|
-
clientInfo, //
|
|
233
|
+
clientInfo, // Store client information
|
|
234
|
+
// Phase 5: Identity state tracking
|
|
235
|
+
identityState: userDid ? "authenticated" : "anonymous",
|
|
236
|
+
oauthIdentity: request.oauthIdentity ?? undefined,
|
|
227
237
|
};
|
|
228
238
|
|
|
229
239
|
this.sessions.set(sessionId, session);
|
|
@@ -246,6 +256,59 @@ export class MCPIRuntimeBase {
|
|
|
246
256
|
};
|
|
247
257
|
}
|
|
248
258
|
|
|
259
|
+
/**
|
|
260
|
+
* Update session identity after OAuth resolution (Phase 5)
|
|
261
|
+
*
|
|
262
|
+
* Called after AgentShield identity/resolve returns a persistent user DID.
|
|
263
|
+
* Updates the session to authenticated state with the resolved DID.
|
|
264
|
+
*
|
|
265
|
+
* @param sessionId - MCP session ID
|
|
266
|
+
* @param userDid - Persistent user DID from AgentShield
|
|
267
|
+
* @param oauthIdentity - OAuth identity information
|
|
268
|
+
* @throws Error if session not found
|
|
269
|
+
*/
|
|
270
|
+
async updateSessionIdentity(
|
|
271
|
+
sessionId: string,
|
|
272
|
+
userDid: string,
|
|
273
|
+
oauthIdentity?: { provider: string; subject: string; email?: string }
|
|
274
|
+
): Promise<void> {
|
|
275
|
+
const session = this.sessions.get(sessionId);
|
|
276
|
+
if (!session) {
|
|
277
|
+
throw new Error(`Session not found: ${sessionId}`);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Update session with resolved identity
|
|
281
|
+
session.userDid = userDid;
|
|
282
|
+
session.identityState = "authenticated";
|
|
283
|
+
if (oauthIdentity) {
|
|
284
|
+
session.oauthIdentity = oauthIdentity;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Update the sessions map
|
|
288
|
+
this.sessions.set(sessionId, session);
|
|
289
|
+
|
|
290
|
+
// Also update UserDidManager cache if available
|
|
291
|
+
if (this.userDidManager) {
|
|
292
|
+
await this.userDidManager.setUserDidForSession(sessionId, userDid, oauthIdentity);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (this.config.audit?.enabled) {
|
|
296
|
+
console.log("[MCP-I] Session identity updated (Phase 5):", {
|
|
297
|
+
sessionId: sessionId.substring(0, 8) + "...",
|
|
298
|
+
userDid: userDid.substring(0, 20) + "...",
|
|
299
|
+
provider: oauthIdentity?.provider,
|
|
300
|
+
identityState: "authenticated",
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Get session by ID
|
|
307
|
+
*/
|
|
308
|
+
getSession(sessionId: string): any | undefined {
|
|
309
|
+
return this.sessions.get(sessionId);
|
|
310
|
+
}
|
|
311
|
+
|
|
249
312
|
/**
|
|
250
313
|
* Process tool call with automatic proof generation
|
|
251
314
|
* Returns clean result only - proof is stored for out-of-band retrieval
|