@kya-os/mcp-i-cloudflare 1.13.0 → 1.13.2
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/helpers/env-mapper.d.ts.map +1 -1
- package/dist/helpers/env-mapper.js +6 -0
- package/dist/helpers/env-mapper.js.map +1 -1
- package/dist/runtime/oauth-handler.d.ts.map +1 -1
- package/dist/runtime/oauth-handler.js +12 -1
- package/dist/runtime/oauth-handler.js.map +1 -1
- package/dist/runtime/oidc/idp-scopes.d.ts +26 -0
- package/dist/runtime/oidc/idp-scopes.d.ts.map +1 -0
- package/dist/runtime/oidc/idp-scopes.js +24 -0
- package/dist/runtime/oidc/idp-scopes.js.map +1 -0
- package/dist/services/batch-queue.d.ts +103 -0
- package/dist/services/batch-queue.d.ts.map +1 -0
- package/dist/services/batch-queue.js +180 -0
- package/dist/services/batch-queue.js.map +1 -0
- package/dist/services/consent.service.d.ts +7 -0
- package/dist/services/consent.service.d.ts.map +1 -1
- package/dist/services/consent.service.js +118 -61
- package/dist/services/consent.service.js.map +1 -1
- package/dist/services/kya-os-events.service.d.ts +131 -0
- package/dist/services/kya-os-events.service.d.ts.map +1 -0
- package/dist/services/kya-os-events.service.js +135 -0
- package/dist/services/kya-os-events.service.js.map +1 -0
- package/dist/services/proof-batch-queue.d.ts +15 -67
- package/dist/services/proof-batch-queue.d.ts.map +1 -1
- package/dist/services/proof-batch-queue.js +12 -147
- package/dist/services/proof-batch-queue.js.map +1 -1
- package/dist/services/proof.service.d.ts +7 -0
- package/dist/services/proof.service.d.ts.map +1 -1
- package/dist/services/proof.service.js +35 -0
- package/dist/services/proof.service.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic Batch Queue (Cloudflare Workers Implementation)
|
|
3
|
+
*
|
|
4
|
+
* Platform-agnostic, type-generic batching for Cloudflare Workers. Collects
|
|
5
|
+
* items in memory and submits them in batches to one or more destinations,
|
|
6
|
+
* without blocking request handling.
|
|
7
|
+
*
|
|
8
|
+
* This is the shared engine behind both the proof batch queue
|
|
9
|
+
* (`proof-batch-queue.ts`) and the KYA-OS activity events emitter
|
|
10
|
+
* (`kya-os-events.service.ts`) so the batching, retry, and flush semantics
|
|
11
|
+
* live in exactly one place.
|
|
12
|
+
*
|
|
13
|
+
* Performance:
|
|
14
|
+
* - Batch size: 10 items (configurable)
|
|
15
|
+
* - Fire-and-forget submission (does not block the caller)
|
|
16
|
+
*
|
|
17
|
+
* Retry Strategy:
|
|
18
|
+
* - Exponential backoff: 1s, 2s, 4s, 8s, 16s
|
|
19
|
+
* - Max retries: 5 (configurable)
|
|
20
|
+
* - Failed items logged and dropped after max retries
|
|
21
|
+
*
|
|
22
|
+
* Note: In Cloudflare Workers, timers do not persist across requests, so
|
|
23
|
+
* automatic flush timers are disabled. Flush is driven by two triggers:
|
|
24
|
+
* within a request when the batch size is reached, and across requests via
|
|
25
|
+
* a scheduled (cron) `flush()` call.
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* Generic Batch Queue.
|
|
29
|
+
*
|
|
30
|
+
* Collects items and submits them in batches to multiple destinations.
|
|
31
|
+
*
|
|
32
|
+
* Note: In Cloudflare Workers, timers don't persist across requests, so
|
|
33
|
+
* automatic flush timers are disabled. Use manual `flush()` calls via cron.
|
|
34
|
+
*/
|
|
35
|
+
export class BatchQueue {
|
|
36
|
+
queue = [];
|
|
37
|
+
pendingBatches = [];
|
|
38
|
+
config;
|
|
39
|
+
closed = false;
|
|
40
|
+
// Stats
|
|
41
|
+
stats = {
|
|
42
|
+
queued: 0,
|
|
43
|
+
submitted: 0,
|
|
44
|
+
failed: 0,
|
|
45
|
+
batchesSubmitted: 0,
|
|
46
|
+
};
|
|
47
|
+
constructor(config) {
|
|
48
|
+
this.config = {
|
|
49
|
+
destinations: config.destinations,
|
|
50
|
+
maxBatchSize: config.maxBatchSize || 10,
|
|
51
|
+
flushIntervalMs: config.flushIntervalMs || 5000,
|
|
52
|
+
maxRetries: config.maxRetries || 5,
|
|
53
|
+
debug: config.debug || false,
|
|
54
|
+
logPrefix: config.logPrefix || "BatchQueue",
|
|
55
|
+
noun: config.noun || "item",
|
|
56
|
+
};
|
|
57
|
+
// Note: Timers are disabled in Cloudflare Workers.
|
|
58
|
+
// Flush must be called manually via cron jobs.
|
|
59
|
+
}
|
|
60
|
+
/** The log prefix, e.g. "[ProofBatchQueue]". */
|
|
61
|
+
get tag() {
|
|
62
|
+
return `[${this.config.logPrefix}]`;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Add an item to the queue.
|
|
66
|
+
*/
|
|
67
|
+
enqueue(item) {
|
|
68
|
+
if (this.closed) {
|
|
69
|
+
console.warn(`${this.tag} Queue is closed, dropping ${this.config.noun}`);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
this.queue.push(item);
|
|
73
|
+
this.stats.queued++;
|
|
74
|
+
if (this.config.debug) {
|
|
75
|
+
console.error(`${this.tag} Enqueued ${this.config.noun} (queue size: ${this.queue.length})`);
|
|
76
|
+
}
|
|
77
|
+
// Flush immediately if batch size reached (Cloudflare Workers compatible)
|
|
78
|
+
if (this.queue.length >= this.config.maxBatchSize) {
|
|
79
|
+
this.flush();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Flush queue immediately (submit all queued items).
|
|
84
|
+
*/
|
|
85
|
+
async flush() {
|
|
86
|
+
if (this.queue.length === 0) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const items = this.queue.splice(0, this.config.maxBatchSize);
|
|
90
|
+
if (this.config.debug) {
|
|
91
|
+
console.error(`${this.tag} Flushing ${items.length} ${this.config.noun}s to ${this.config.destinations.length} destinations`);
|
|
92
|
+
}
|
|
93
|
+
// Submit to all destinations
|
|
94
|
+
for (const destination of this.config.destinations) {
|
|
95
|
+
const batch = {
|
|
96
|
+
items: [...items], // Copy items for each destination
|
|
97
|
+
destination,
|
|
98
|
+
retryCount: 0,
|
|
99
|
+
};
|
|
100
|
+
this.submitBatch(batch); // Fire-and-forget
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Submit a batch to a destination (with retries).
|
|
105
|
+
*/
|
|
106
|
+
async submitBatch(batch) {
|
|
107
|
+
try {
|
|
108
|
+
await batch.destination.submit(batch.items);
|
|
109
|
+
this.stats.submitted += batch.items.length;
|
|
110
|
+
this.stats.batchesSubmitted++;
|
|
111
|
+
if (this.config.debug) {
|
|
112
|
+
console.error(`${this.tag} Successfully submitted ${batch.items.length} ${this.config.noun}s to ${batch.destination.name}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
console.error(`${this.tag} Failed to submit to ${batch.destination.name}:`, error);
|
|
117
|
+
// Retry with exponential backoff
|
|
118
|
+
if (batch.retryCount < this.config.maxRetries) {
|
|
119
|
+
batch.retryCount++;
|
|
120
|
+
const backoffMs = Math.min(1000 * Math.pow(2, batch.retryCount - 1), 16000);
|
|
121
|
+
batch.nextRetryAt = Date.now() + backoffMs;
|
|
122
|
+
this.pendingBatches.push(batch);
|
|
123
|
+
if (this.config.debug) {
|
|
124
|
+
console.error(`${this.tag} Scheduling retry ${batch.retryCount}/${this.config.maxRetries} in ${backoffMs}ms`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
// Max retries exceeded, drop batch
|
|
129
|
+
this.stats.failed += batch.items.length;
|
|
130
|
+
console.error(`${this.tag} Max retries exceeded for ${batch.destination.name}, dropping ${batch.items.length} ${this.config.noun}s`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Retry pending batches that are ready.
|
|
136
|
+
* Called by cron job or manually.
|
|
137
|
+
*/
|
|
138
|
+
async retryPending() {
|
|
139
|
+
const now = Date.now();
|
|
140
|
+
const retryBatches = this.pendingBatches.filter((batch) => batch.nextRetryAt && batch.nextRetryAt <= now);
|
|
141
|
+
if (retryBatches.length > 0) {
|
|
142
|
+
// Remove from pending
|
|
143
|
+
this.pendingBatches = this.pendingBatches.filter((batch) => !retryBatches.includes(batch));
|
|
144
|
+
// Retry each batch
|
|
145
|
+
for (const batch of retryBatches) {
|
|
146
|
+
this.submitBatch(batch); // Fire-and-forget
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Close queue and flush remaining items.
|
|
152
|
+
*/
|
|
153
|
+
async close() {
|
|
154
|
+
if (this.closed) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
this.closed = true;
|
|
158
|
+
// Flush remaining items
|
|
159
|
+
if (this.queue.length > 0) {
|
|
160
|
+
await this.flush();
|
|
161
|
+
}
|
|
162
|
+
// Wait for pending batches (with timeout)
|
|
163
|
+
const timeout = 5000; // 5 seconds
|
|
164
|
+
const startTime = Date.now();
|
|
165
|
+
while (this.pendingBatches.length > 0 && Date.now() - startTime < timeout) {
|
|
166
|
+
await this.retryPending();
|
|
167
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
168
|
+
}
|
|
169
|
+
if (this.pendingBatches.length > 0) {
|
|
170
|
+
console.warn(`${this.tag} Closing with ${this.pendingBatches.length} pending batches (timed out)`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Get queue statistics.
|
|
175
|
+
*/
|
|
176
|
+
getStats() {
|
|
177
|
+
return { ...this.stats };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=batch-queue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"batch-queue.js","sourceRoot":"","sources":["../../src/services/batch-queue.ts"],"names":[],"mappings":"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"}
|
|
@@ -384,6 +384,13 @@ export declare class ConsentService {
|
|
|
384
384
|
* @returns Parsed body object
|
|
385
385
|
*/
|
|
386
386
|
private parseRequestBody;
|
|
387
|
+
/**
|
|
388
|
+
* Interpret the `CONSENT_REQUIRE_IDENTITY` env override that gates the
|
|
389
|
+
* identity-required refusal in `handleApproval`. Defaults to enabled - only
|
|
390
|
+
* an explicit `"false"`/`"0"` disables it, mirroring the escape-hatch
|
|
391
|
+
* convention used for `MCPI_HTTP_CHALLENGE` in config.ts.
|
|
392
|
+
*/
|
|
393
|
+
private isIdentityRequired;
|
|
387
394
|
/**
|
|
388
395
|
* Handle consent approval
|
|
389
396
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consent.service.d.ts","sourceRoot":"","sources":["../../src/services/consent.service.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"consent.service.d.ts","sourceRoot":"","sources":["../../src/services/consent.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,6 +12,7 @@ import { DEFAULT_AGENTSHIELD_URL, DEFAULT_SESSION_CACHE_TTL, KEY_PAIR_TTL_SECOND
|
|
|
12
12
|
import { STORAGE_KEYS } from "../constants/storage-keys";
|
|
13
13
|
import { loadDay0Config, getDelegationFieldName } from "../utils/day0-config";
|
|
14
14
|
import { buildProviderAuthorizeUrl } from "../runtime/oidc/authorize-url.js";
|
|
15
|
+
import { resolveIdpScopes } from "../runtime/oidc/idp-scopes.js";
|
|
15
16
|
import { validateConsentApprovalRequest, } from "@kya-os/contracts/consent";
|
|
16
17
|
import { AGENTSHIELD_ENDPOINTS, createDelegationAPIResponseSchema, createDelegationResponseSchema, } from "@kya-os/contracts/agentshield-api";
|
|
17
18
|
import { createUnsignedVCJWT, completeVCJWT, parseVCJWT, generateDidKeyFromBase64, createDelegationVerifier, createDidKeyResolver, logger, base64urlEncodeFromBytes, base64urlDecodeToBytes, bytesToBase64, wrapDelegationAsVC, } from "@kya-os/mcp";
|
|
@@ -1154,13 +1155,19 @@ export class ConsentService {
|
|
|
1154
1155
|
!providerConfig.proxyMode) {
|
|
1155
1156
|
// Use providerConfig.clientId from AgentShield dashboard config
|
|
1156
1157
|
const oauthClientId = providerConfig.clientId || projectId;
|
|
1158
|
+
// The `scopes` argument holds MCP-I delegation scopes (e.g. "greet:execute"),
|
|
1159
|
+
// which the identity provider has never heard of. Only the provider's own
|
|
1160
|
+
// scopes belong in its authorization URL; delegation scopes already travel
|
|
1161
|
+
// separately in `stateData.scopes` above, for the callback to mint the
|
|
1162
|
+
// delegation.
|
|
1163
|
+
const idpScopes = resolveIdpScopes(providerConfig);
|
|
1157
1164
|
logger.debug("[ConsentService] Using direct OAuth mode (PKCE)", {
|
|
1158
1165
|
provider: provider || "unknown",
|
|
1159
1166
|
authorizationUrl: providerConfig.authorizationUrl,
|
|
1160
1167
|
supportsPKCE: providerConfig.supportsPKCE,
|
|
1161
1168
|
clientId: oauthClientId.substring(0, 8) + "...",
|
|
1162
1169
|
});
|
|
1163
|
-
return this.buildDirectOAuthUrl(providerConfig, oauthClientId, `${serverUrl}/oauth/callback`,
|
|
1170
|
+
return this.buildDirectOAuthUrl(providerConfig, oauthClientId, `${serverUrl}/oauth/callback`, idpScopes, stateParam, codeChallenge);
|
|
1164
1171
|
}
|
|
1165
1172
|
// Phase 3: Validate custom parameters don't conflict with reserved parameters
|
|
1166
1173
|
const RESERVED_PARAMS = [
|
|
@@ -1195,7 +1202,9 @@ export class ConsentService {
|
|
|
1195
1202
|
oauthUrl.searchParams.set("response_type", "code");
|
|
1196
1203
|
oauthUrl.searchParams.set("client_id", projectId); // Use projectId as client_id
|
|
1197
1204
|
oauthUrl.searchParams.set("redirect_uri", `${serverUrl}/oauth/callback`);
|
|
1198
|
-
|
|
1205
|
+
// Same separation as the direct-PKCE branch above: `scopes` is the MCP-I
|
|
1206
|
+
// delegation scopes, not what belongs in the IdP-facing `scope` param.
|
|
1207
|
+
oauthUrl.searchParams.set("scope", resolveIdpScopes(providerConfig).join(" "));
|
|
1199
1208
|
oauthUrl.searchParams.set("state", stateParam);
|
|
1200
1209
|
// ✅ Pass provider to AgentShield bouncer so it selects the correct auth method
|
|
1201
1210
|
// This is critical when multiple providers are configured for a project
|
|
@@ -3219,6 +3228,19 @@ export class ConsentService {
|
|
|
3219
3228
|
throw new Error(`Failed to parse request body: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3220
3229
|
}
|
|
3221
3230
|
}
|
|
3231
|
+
/**
|
|
3232
|
+
* Interpret the `CONSENT_REQUIRE_IDENTITY` env override that gates the
|
|
3233
|
+
* identity-required refusal in `handleApproval`. Defaults to enabled - only
|
|
3234
|
+
* an explicit `"false"`/`"0"` disables it, mirroring the escape-hatch
|
|
3235
|
+
* convention used for `MCPI_HTTP_CHALLENGE` in config.ts.
|
|
3236
|
+
*/
|
|
3237
|
+
isIdentityRequired() {
|
|
3238
|
+
const raw = this.env.CONSENT_REQUIRE_IDENTITY;
|
|
3239
|
+
if (raw === undefined || raw === null)
|
|
3240
|
+
return true;
|
|
3241
|
+
const normalized = String(raw).trim().toLowerCase();
|
|
3242
|
+
return normalized !== "false" && normalized !== "0";
|
|
3243
|
+
}
|
|
3222
3244
|
/**
|
|
3223
3245
|
* Handle consent approval
|
|
3224
3246
|
*
|
|
@@ -3453,11 +3475,30 @@ export class ConsentService {
|
|
|
3453
3475
|
// ✅ Lazy initialization with projectId
|
|
3454
3476
|
const auditService = await this.getAuditService(projectId);
|
|
3455
3477
|
// Check if user needs credentials before delegation
|
|
3456
|
-
// Skip credential requirement for consent-only mode (provider_type: 'none')
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3478
|
+
// Skip credential requirement for consent-only mode (provider_type: 'none').
|
|
3479
|
+
// Read the EFFECTIVE provider_type off bodyObj, not the `providerType`
|
|
3480
|
+
// captured above: the post-OAuth/post-credential clickwrap branches above
|
|
3481
|
+
// rewrite bodyObj.provider_type to 'oauth'/'password' while `providerType`
|
|
3482
|
+
// stays the original raw 'none'. Gating on the stale value would treat
|
|
3483
|
+
// those already-authenticated clickwrap submissions as consent-only and
|
|
3484
|
+
// skip the identity check entirely.
|
|
3485
|
+
const effectiveProviderType = bodyObj.provider_type;
|
|
3486
|
+
const isConsentOnlyMode = effectiveProviderType === CONSENT_PROVIDER_TYPES.NONE;
|
|
3487
|
+
// Resolve identity once, ahead of the gate below, using the same priority
|
|
3488
|
+
// createDelegation used to apply internally (request.user_did first, then
|
|
3489
|
+
// session lookup). The resolved value is threaded into createDelegation so
|
|
3490
|
+
// it is never resolved a second time.
|
|
3491
|
+
let resolvedUserDid = approvalRequest.user_did;
|
|
3492
|
+
if (!resolvedUserDid && approvalRequest.session_id) {
|
|
3493
|
+
try {
|
|
3494
|
+
resolvedUserDid =
|
|
3495
|
+
(await this.getUserDidForSession(approvalRequest.session_id, approvalRequest.oauth_identity || undefined)) ?? undefined;
|
|
3496
|
+
}
|
|
3497
|
+
catch (error) {
|
|
3498
|
+
logger.debug("[ConsentService] Failed to resolve userDid ahead of identity gate:", error);
|
|
3499
|
+
}
|
|
3500
|
+
}
|
|
3501
|
+
const needsCredentials = !isConsentOnlyMode && !resolvedUserDid;
|
|
3461
3502
|
if (needsCredentials && auditService) {
|
|
3462
3503
|
await auditService
|
|
3463
3504
|
.logCredentialRequired({
|
|
@@ -3475,12 +3516,25 @@ export class ConsentService {
|
|
|
3475
3516
|
error: err instanceof Error ? err.message : String(err),
|
|
3476
3517
|
});
|
|
3477
3518
|
});
|
|
3478
|
-
|
|
3479
|
-
|
|
3519
|
+
}
|
|
3520
|
+
// Fail closed: an authenticated-provider approval with no resolvable user
|
|
3521
|
+
// identity must not mint a delegation. Consent-only mode is exempt - it
|
|
3522
|
+
// legitimately has no user identity. Gated on CONSENT_REQUIRE_IDENTITY so
|
|
3523
|
+
// a bad interaction can be switched off in production without a redeploy;
|
|
3524
|
+
// defaults to enabled.
|
|
3525
|
+
if (needsCredentials && this.isIdentityRequired()) {
|
|
3526
|
+
return new Response(JSON.stringify({
|
|
3527
|
+
success: false,
|
|
3528
|
+
error: "User identity is required to approve this request",
|
|
3529
|
+
error_code: "identity_required",
|
|
3530
|
+
}), {
|
|
3531
|
+
status: 403,
|
|
3532
|
+
headers: { "Content-Type": "application/json" },
|
|
3533
|
+
});
|
|
3480
3534
|
}
|
|
3481
3535
|
// Create delegation via AgentShield API
|
|
3482
3536
|
logger.debug("[ConsentService] Creating delegation...");
|
|
3483
|
-
const delegationResult = await this.createDelegation(approvalRequest, resolveExpirationDays(consentConfig.expirationDays));
|
|
3537
|
+
const delegationResult = await this.createDelegation(approvalRequest, resolveExpirationDays(consentConfig.expirationDays), resolvedUserDid ?? null);
|
|
3484
3538
|
if (!delegationResult.success) {
|
|
3485
3539
|
logger.error("[ConsentService] Delegation creation failed:", {
|
|
3486
3540
|
error: delegationResult.error,
|
|
@@ -3507,19 +3561,9 @@ export class ConsentService {
|
|
|
3507
3561
|
// ✅ After successful delegation creation - log audit events
|
|
3508
3562
|
if (auditService && delegationResult.success) {
|
|
3509
3563
|
try {
|
|
3510
|
-
//
|
|
3511
|
-
//
|
|
3512
|
-
|
|
3513
|
-
if (approvalRequest.session_id) {
|
|
3514
|
-
try {
|
|
3515
|
-
userDid =
|
|
3516
|
-
(await this.getUserDidForSession(approvalRequest.session_id, approvalRequest.oauth_identity || undefined)) ?? undefined; // Phase 5: Convert null to undefined
|
|
3517
|
-
}
|
|
3518
|
-
catch (error) {
|
|
3519
|
-
logger.warn("[ConsentService] Failed to get userDid for lifecycle recording:", error);
|
|
3520
|
-
// Continue without userDid - audit events can still be logged
|
|
3521
|
-
}
|
|
3522
|
-
}
|
|
3564
|
+
// Reuse the identity resolved ahead of the gate above - do not
|
|
3565
|
+
// read KV a second time for the same session.
|
|
3566
|
+
const userDid = resolvedUserDid;
|
|
3523
3567
|
await auditService.logConsentApproval({
|
|
3524
3568
|
sessionId: approvalRequest.session_id,
|
|
3525
3569
|
userDid,
|
|
@@ -3583,7 +3627,7 @@ export class ConsentService {
|
|
|
3583
3627
|
* @param request - Approval request
|
|
3584
3628
|
* @returns Delegation creation result
|
|
3585
3629
|
*/
|
|
3586
|
-
async createDelegation(request, expirationDaysOverride) {
|
|
3630
|
+
async createDelegation(request, expirationDaysOverride, resolvedUserDid) {
|
|
3587
3631
|
const agentShieldUrl = this.env.AGENTSHIELD_API_URL || DEFAULT_AGENTSHIELD_URL;
|
|
3588
3632
|
const apiKey = this.env.AGENTSHIELD_API_KEY;
|
|
3589
3633
|
if (!apiKey) {
|
|
@@ -3598,46 +3642,59 @@ export class ConsentService {
|
|
|
3598
3642
|
// Load Day0 configuration to determine field name and API capabilities
|
|
3599
3643
|
await loadDay0Config(this.env.DELEGATION_STORAGE);
|
|
3600
3644
|
const fieldName = await getDelegationFieldName(this.env.DELEGATION_STORAGE);
|
|
3601
|
-
// Get userDID
|
|
3602
|
-
//
|
|
3603
|
-
//
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
if (
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
hasOAuthIdentity: !!request.oauth_identity,
|
|
3611
|
-
oauthProvider: request.oauth_identity?.provider,
|
|
3612
|
-
hasStorage: !!this.env.DELEGATION_STORAGE,
|
|
3613
|
-
});
|
|
3614
|
-
// Pass OAuth identity if available in approval request (can be null/undefined)
|
|
3615
|
-
// getUserDidForSession can work without DELEGATION_STORAGE (uses in-memory UserDidManager)
|
|
3616
|
-
// Phase 5: Returns null if no identity found (session stays anonymous)
|
|
3617
|
-
userDid =
|
|
3618
|
-
(await this.getUserDidForSession(request.session_id, request.oauth_identity || undefined // Explicitly handle null as undefined
|
|
3619
|
-
)) ?? undefined;
|
|
3620
|
-
logger.debug("[ConsentService] User DID retrieved from storage:", {
|
|
3621
|
-
userDid: userDid?.substring(0, 20) + "...",
|
|
3622
|
-
hasUserDid: !!userDid,
|
|
3623
|
-
});
|
|
3624
|
-
}
|
|
3625
|
-
catch (error) {
|
|
3626
|
-
logger.debug("[ConsentService] Failed to get/generate userDid:", error);
|
|
3627
|
-
// Continue without userDid - delegation will work without user_identifier
|
|
3628
|
-
// This is valid for non-OAuth scenarios, but we should log this as a warning
|
|
3629
|
-
logger.warn("[ConsentService] Delegation will be created without user_identifier - this may affect user tracking");
|
|
3630
|
-
}
|
|
3631
|
-
}
|
|
3632
|
-
else if (userDid) {
|
|
3633
|
-
// userDid was provided in request (e.g., from credential auth flow)
|
|
3634
|
-
logger.debug("[ConsentService] Using provided user_did from request:", {
|
|
3635
|
-
userDid: userDid.substring(0, 20) + "...",
|
|
3636
|
-
source: "request.user_did",
|
|
3645
|
+
// Get userDID. If the caller (handleApproval) already resolved it, use that
|
|
3646
|
+
// directly and skip the lookup below entirely - it was already attempted
|
|
3647
|
+
// with the same priority (request.user_did first, then session storage),
|
|
3648
|
+
// and repeating it would read KV a second time for no benefit.
|
|
3649
|
+
let userDid;
|
|
3650
|
+
if (resolvedUserDid !== undefined) {
|
|
3651
|
+
userDid = resolvedUserDid ?? undefined;
|
|
3652
|
+
logger.debug("[ConsentService] Using pre-resolved userDid:", {
|
|
3653
|
+
hasUserDid: !!userDid,
|
|
3637
3654
|
});
|
|
3638
3655
|
}
|
|
3639
3656
|
else {
|
|
3640
|
-
|
|
3657
|
+
// FIRST check if passed in request, THEN fallback to session storage
|
|
3658
|
+
// request.user_did takes priority to avoid KV eventual consistency issues
|
|
3659
|
+
// This fixes the bug where credential auth resolves userDid but it's not found in storage
|
|
3660
|
+
userDid = request.user_did;
|
|
3661
|
+
// Only fetch from storage if not already provided in request
|
|
3662
|
+
if (!userDid && request.session_id) {
|
|
3663
|
+
try {
|
|
3664
|
+
logger.debug("[ConsentService] Getting User DID for session:", {
|
|
3665
|
+
sessionId: request.session_id.substring(0, 20) + "...",
|
|
3666
|
+
hasOAuthIdentity: !!request.oauth_identity,
|
|
3667
|
+
oauthProvider: request.oauth_identity?.provider,
|
|
3668
|
+
hasStorage: !!this.env.DELEGATION_STORAGE,
|
|
3669
|
+
});
|
|
3670
|
+
// Pass OAuth identity if available in approval request (can be null/undefined)
|
|
3671
|
+
// getUserDidForSession can work without DELEGATION_STORAGE (uses in-memory UserDidManager)
|
|
3672
|
+
// Phase 5: Returns null if no identity found (session stays anonymous)
|
|
3673
|
+
userDid =
|
|
3674
|
+
(await this.getUserDidForSession(request.session_id, request.oauth_identity || undefined // Explicitly handle null as undefined
|
|
3675
|
+
)) ?? undefined;
|
|
3676
|
+
logger.debug("[ConsentService] User DID retrieved from storage:", {
|
|
3677
|
+
userDid: userDid?.substring(0, 20) + "...",
|
|
3678
|
+
hasUserDid: !!userDid,
|
|
3679
|
+
});
|
|
3680
|
+
}
|
|
3681
|
+
catch (error) {
|
|
3682
|
+
logger.debug("[ConsentService] Failed to get/generate userDid:", error);
|
|
3683
|
+
// Continue without userDid - delegation will work without user_identifier
|
|
3684
|
+
// This is valid for non-OAuth scenarios, but we should log this as a warning
|
|
3685
|
+
logger.warn("[ConsentService] Delegation will be created without user_identifier - this may affect user tracking");
|
|
3686
|
+
}
|
|
3687
|
+
}
|
|
3688
|
+
else if (userDid) {
|
|
3689
|
+
// userDid was provided in request (e.g., from credential auth flow)
|
|
3690
|
+
logger.debug("[ConsentService] Using provided user_did from request:", {
|
|
3691
|
+
userDid: userDid.substring(0, 20) + "...",
|
|
3692
|
+
source: "request.user_did",
|
|
3693
|
+
});
|
|
3694
|
+
}
|
|
3695
|
+
else {
|
|
3696
|
+
logger.debug("[ConsentService] No session_id provided - skipping User DID generation");
|
|
3697
|
+
}
|
|
3641
3698
|
}
|
|
3642
3699
|
const expiresInDays = expirationDaysOverride ?? DEFAULT_EXPIRATION_DAYS;
|
|
3643
3700
|
// Phase 2 VC-Only: Issue Delegation VC if we have a session and userDid
|