@kya-os/mcp-i-cloudflare 1.9.7 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/dist/adapter.d.ts.map +1 -1
  2. package/dist/adapter.js +47 -16
  3. package/dist/adapter.js.map +1 -1
  4. package/dist/agent.d.ts.map +1 -1
  5. package/dist/agent.js +181 -50
  6. package/dist/agent.js.map +1 -1
  7. package/dist/app.d.ts.map +1 -1
  8. package/dist/app.js +86 -3
  9. package/dist/app.js.map +1 -1
  10. package/dist/config.d.ts +54 -0
  11. package/dist/config.d.ts.map +1 -1
  12. package/dist/config.js +58 -0
  13. package/dist/config.js.map +1 -1
  14. package/dist/constants/storage-keys.d.ts +6 -0
  15. package/dist/constants/storage-keys.d.ts.map +1 -1
  16. package/dist/constants/storage-keys.js +6 -0
  17. package/dist/constants/storage-keys.js.map +1 -1
  18. package/dist/delegation-http/anchor-do.d.ts +84 -0
  19. package/dist/delegation-http/anchor-do.d.ts.map +1 -0
  20. package/dist/delegation-http/anchor-do.js +253 -0
  21. package/dist/delegation-http/anchor-do.js.map +1 -0
  22. package/dist/delegation-http/anchor-routing.d.ts +20 -0
  23. package/dist/delegation-http/anchor-routing.d.ts.map +1 -0
  24. package/dist/delegation-http/anchor-routing.js +27 -0
  25. package/dist/delegation-http/anchor-routing.js.map +1 -0
  26. package/dist/delegation-http/bearer-retry.d.ts +54 -0
  27. package/dist/delegation-http/bearer-retry.d.ts.map +1 -0
  28. package/dist/delegation-http/bearer-retry.js +195 -0
  29. package/dist/delegation-http/bearer-retry.js.map +1 -0
  30. package/dist/delegation-http/challenge-middleware.d.ts +28 -0
  31. package/dist/delegation-http/challenge-middleware.d.ts.map +1 -0
  32. package/dist/delegation-http/challenge-middleware.js +190 -0
  33. package/dist/delegation-http/challenge-middleware.js.map +1 -0
  34. package/dist/delegation-http/challenge.d.ts +86 -0
  35. package/dist/delegation-http/challenge.d.ts.map +1 -0
  36. package/dist/delegation-http/challenge.js +173 -0
  37. package/dist/delegation-http/challenge.js.map +1 -0
  38. package/dist/delegation-http/consent-anchor-bridge.d.ts +35 -0
  39. package/dist/delegation-http/consent-anchor-bridge.d.ts.map +1 -0
  40. package/dist/delegation-http/consent-anchor-bridge.js +355 -0
  41. package/dist/delegation-http/consent-anchor-bridge.js.map +1 -0
  42. package/dist/delegation-http/constants.d.ts +51 -0
  43. package/dist/delegation-http/constants.d.ts.map +1 -0
  44. package/dist/delegation-http/constants.js +60 -0
  45. package/dist/delegation-http/constants.js.map +1 -0
  46. package/dist/delegation-http/hash.d.ts +21 -0
  47. package/dist/delegation-http/hash.d.ts.map +1 -0
  48. package/dist/delegation-http/hash.js +92 -0
  49. package/dist/delegation-http/hash.js.map +1 -0
  50. package/dist/delegation-http/pickup-routes.d.ts +42 -0
  51. package/dist/delegation-http/pickup-routes.d.ts.map +1 -0
  52. package/dist/delegation-http/pickup-routes.js +79 -0
  53. package/dist/delegation-http/pickup-routes.js.map +1 -0
  54. package/dist/delegation-http/register.d.ts +19 -0
  55. package/dist/delegation-http/register.d.ts.map +1 -0
  56. package/dist/delegation-http/register.js +72 -0
  57. package/dist/delegation-http/register.js.map +1 -0
  58. package/dist/delegation-http/sid.d.ts +20 -0
  59. package/dist/delegation-http/sid.d.ts.map +1 -0
  60. package/dist/delegation-http/sid.js +32 -0
  61. package/dist/delegation-http/sid.js.map +1 -0
  62. package/dist/delegation-http/types.d.ts +64 -0
  63. package/dist/delegation-http/types.d.ts.map +1 -0
  64. package/dist/delegation-http/types.js +11 -0
  65. package/dist/delegation-http/types.js.map +1 -0
  66. package/dist/helpers/env-mapper.d.ts.map +1 -1
  67. package/dist/helpers/env-mapper.js +24 -0
  68. package/dist/helpers/env-mapper.js.map +1 -1
  69. package/dist/index.d.ts +5 -2
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +10 -2
  72. package/dist/index.js.map +1 -1
  73. package/dist/providers/crypto.d.ts +1 -1
  74. package/dist/providers/crypto.d.ts.map +1 -1
  75. package/dist/providers/crypto.js +1 -1
  76. package/dist/providers/crypto.js.map +1 -1
  77. package/dist/providers/kv-identity.d.ts +2 -2
  78. package/dist/providers/kv-identity.d.ts.map +1 -1
  79. package/dist/providers/kv-identity.js +1 -1
  80. package/dist/providers/kv-identity.js.map +1 -1
  81. package/dist/providers/storage.d.ts +1 -1
  82. package/dist/providers/storage.d.ts.map +1 -1
  83. package/dist/providers/storage.js +1 -1
  84. package/dist/providers/storage.js.map +1 -1
  85. package/dist/runtime/audit-logger.d.ts +1 -1
  86. package/dist/runtime/audit-logger.d.ts.map +1 -1
  87. package/dist/runtime/oauth-handler.d.ts.map +1 -1
  88. package/dist/runtime/oauth-handler.js +31 -3
  89. package/dist/runtime/oauth-handler.js.map +1 -1
  90. package/dist/runtime/oidc/authorize-url.d.ts +19 -0
  91. package/dist/runtime/oidc/authorize-url.d.ts.map +1 -0
  92. package/dist/runtime/oidc/authorize-url.js +94 -0
  93. package/dist/runtime/oidc/authorize-url.js.map +1 -0
  94. package/dist/runtime/oidc/token-exchange.d.ts +62 -0
  95. package/dist/runtime/oidc/token-exchange.d.ts.map +1 -0
  96. package/dist/runtime/oidc/token-exchange.js +75 -0
  97. package/dist/runtime/oidc/token-exchange.js.map +1 -0
  98. package/dist/runtime.d.ts +1 -1
  99. package/dist/runtime.d.ts.map +1 -1
  100. package/dist/services/consent-audit.service.d.ts +1 -1
  101. package/dist/services/consent-audit.service.d.ts.map +1 -1
  102. package/dist/services/consent-audit.service.js.map +1 -1
  103. package/dist/services/consent.service.d.ts +2 -2
  104. package/dist/services/consent.service.d.ts.map +1 -1
  105. package/dist/services/consent.service.js +39 -50
  106. package/dist/services/consent.service.js.map +1 -1
  107. package/dist/services/delegation.service.d.ts +9 -0
  108. package/dist/services/delegation.service.d.ts.map +1 -1
  109. package/dist/services/delegation.service.js +21 -7
  110. package/dist/services/delegation.service.js.map +1 -1
  111. package/dist/services/oauth-security.service.d.ts.map +1 -1
  112. package/dist/services/oauth-security.service.js +6 -10
  113. package/dist/services/oauth-security.service.js.map +1 -1
  114. package/dist/services/provider-loader.service.js +1 -1
  115. package/dist/services/provider-loader.service.js.map +1 -1
  116. package/dist/types.d.ts +5 -1
  117. package/dist/types.d.ts.map +1 -1
  118. package/dist/utils/known-clients.d.ts.map +1 -1
  119. package/dist/utils/known-clients.js +19 -10
  120. package/dist/utils/known-clients.js.map +1 -1
  121. package/package.json +3 -1
@@ -0,0 +1,190 @@
1
+ /**
2
+ * draft-kya-http-02 delegation layer — §8.1 challenge emitter middleware.
3
+ *
4
+ * Triggers on JSON-RPC responses whose `result._meta.errorType ===
5
+ * "authorization_required"` (the existing `DelegationRequiredError` → `result._meta`
6
+ * mapping in `adapter.ts`), mints a sid, creates the pickup anchor (which MUST exist
7
+ * before the 401 leaves), signs the `challenge_proof`, and emits the §8.1 401 in
8
+ * `negotiated`/`always` mode. Returns `null` to pass the original response through.
9
+ *
10
+ * @module @kya-os/mcp-i-cloudflare/delegation-http/challenge-middleware
11
+ */
12
+ import { sha256Hex } from "./hash";
13
+ import { INTERNAL_PICKUP_CREATE } from "./constants";
14
+ import { buildChallengeResponse, buildFetcherChallengeResponse, buildConsentUri, signChallengeProof, } from "./challenge";
15
+ /**
16
+ * Inspect a cloned JSON-RPC response body for the `authorization_required` trigger.
17
+ * Returns the parsed meta when present, otherwise `null` (pass through untouched).
18
+ */
19
+ async function readAuthorizationRequiredMeta(response) {
20
+ let parsed;
21
+ try {
22
+ parsed = await response.clone().json();
23
+ }
24
+ catch {
25
+ // Non-JSON or unreadable body: not a delegation error.
26
+ return null;
27
+ }
28
+ const result = parsed
29
+ ?.result;
30
+ const meta = result?._meta;
31
+ if (!meta || meta.errorType !== "authorization_required") {
32
+ return null;
33
+ }
34
+ return meta;
35
+ }
36
+ /**
37
+ * INTERACTIVE AI-agent fetcher User-Agents — agents that fetch on behalf of a human in
38
+ * a live session (so they can relay a consent link) but collapse 4xx/5xx bodies and
39
+ * never surface a 401 challenge to the LLM. Deliberately EXCLUDES indexing crawlers
40
+ * (e.g. GPTBot, OAI-SearchBot, PerplexityBot): a crawler has no user to relay consent
41
+ * to, so it stays on the spec path — matching the engine PEPs' `ai_agent` (NOT bot)
42
+ * contract. TRANSITIONAL worker-local heuristic; the durable signal is the
43
+ * `KYA-Accept-Challenge` opt-in header, and this list retires as frameworks adopt it.
44
+ */
45
+ const AI_AGENT_FETCHER_UA = /\b(claude-user|chatgpt-user)\b/i;
46
+ /**
47
+ * A cooperative agent fetcher that should receive the body-readable HTTP 200 envelope
48
+ * instead of a 401 it would silently drop: the caller opts in via `KYA-Accept-Challenge`
49
+ * (any non-empty value — the durable, primary signal), OR its User-Agent matches a known
50
+ * agent fetcher (the transitional heuristic). Mirrors the other PEPs' `serveBodyReadable200`
51
+ * predicate. A cooperative-UX bridge, NOT an access control.
52
+ */
53
+ function callerWantsBodyReadable200(request) {
54
+ const accept = request.headers.get("KYA-Accept-Challenge");
55
+ if (accept && accept.trim().length > 0) {
56
+ return true;
57
+ }
58
+ const ua = request.headers.get("User-Agent") ?? "";
59
+ return AI_AGENT_FETCHER_UA.test(ua);
60
+ }
61
+ /**
62
+ * A signature-capable caller (RFC 9421 `Signature-Input` with a `kya` label) that can
63
+ * read a 4xx and handle the dual `WWW-Authenticate` wire — it keeps the spec 401.
64
+ */
65
+ function callerIsSignatureCapable(request) {
66
+ const signatureInput = request.headers.get("Signature-Input");
67
+ return !!signatureInput && /(^|,)\s*kya\s*=/.test(signatureInput);
68
+ }
69
+ /** Pull the required scopes out of the adapter meta, defaulting to an empty list. */
70
+ function readRequiredScopes(meta) {
71
+ if (Array.isArray(meta.requiredScopes)) {
72
+ return meta.requiredScopes.filter((scope) => typeof scope === "string");
73
+ }
74
+ return [];
75
+ }
76
+ /**
77
+ * Derive a deterministic anchor sid (`kya-` + 32 hex) from the MCP session, the
78
+ * required scopes, AND the `minAssurance` (W1 creation-bounding). Repeated
79
+ * `authorization_required` results for the same caller+scopes+assurance reuse one
80
+ * `pickup:<sid>` anchor instead of minting a fresh durable object per request. A
81
+ * session-less caller collapses to one anchor per (scope-set, assurance); the MCP
82
+ * session id (when present) separates clients.
83
+ *
84
+ * `minAssurance` is part of the key because `reduceCreate` reuses a still-live anchor
85
+ * without mutating its fields: if it were omitted, a later challenge advertising a new
86
+ * `min_assurance` would reuse an anchor (and consent surface) still carrying the old
87
+ * value — so a changed assurance MUST derive a distinct anchor.
88
+ */
89
+ function deriveAnchorSid(request, requiredScopes, minAssurance) {
90
+ const session = request.headers.get("mcp-session-id") ?? "";
91
+ const scopeKey = [...requiredScopes].sort().join(" ");
92
+ return `kya-${sha256Hex(`${session}\n${scopeKey}\n${minAssurance ?? ""}`).slice(0, 32)}`;
93
+ }
94
+ /**
95
+ * Emit a §8.1 delegation challenge in place of an `authorization_required` MCP
96
+ * response, or return `null` to pass the original response through untouched.
97
+ *
98
+ * `negotiated` (default): when the caller is NOT kya-capable, the ORIGINAL MCP error
99
+ * response is returned (so existing MCP/OAuth clients still see the unmodified error),
100
+ * never a 401. `always`: every `authorization_required` becomes a 401.
101
+ */
102
+ export async function maybeEmitDelegationChallenge(request, response, env, cfg, deps) {
103
+ const meta = await readAuthorizationRequiredMeta(response);
104
+ if (!meta) {
105
+ // Not a delegation error — pass through untouched.
106
+ return null;
107
+ }
108
+ // Selection: a cooperative agent fetcher gets the body-readable 200; a
109
+ // signature-capable client gets the spec 401. In `negotiated` mode a caller that is
110
+ // NEITHER is handed back the original MCP error (never a challenge it can't read).
111
+ const wantsBodyReadable200 = callerWantsBodyReadable200(request);
112
+ const signatureCapable = callerIsSignatureCapable(request);
113
+ if (cfg.mode === "negotiated" && !wantsBodyReadable200 && !signatureCapable) {
114
+ return response;
115
+ }
116
+ // W4: pin the challenge host to the configured realm when set, falling back to the
117
+ // request Host only when unset — so a Host-header-reflecting deployment cannot get
118
+ // an org-signed challenge (and consent/pickup links) pointing at an attacker host.
119
+ const host = cfg.realm || new URL(request.url).host;
120
+ const requiredScopes = readRequiredScopes(meta);
121
+ const minAssurance = typeof meta.minAssurance === "string" ? meta.minAssurance : undefined;
122
+ const sid = deriveAnchorSid(request, requiredScopes, minAssurance);
123
+ const verdict = {
124
+ requiredScopes,
125
+ minAssurance,
126
+ authorizationHost: host,
127
+ realm: host,
128
+ sid,
129
+ pollIntervalS: cfg.pollIntervalS,
130
+ };
131
+ // The anchor MUST exist before the 401 leaves: a caller could poll the pickup URL
132
+ // the instant it reads the challenge. Create-then-respond ordering is enforced here
133
+ // by awaiting the DO create — AND verifying it persisted (201) — before
134
+ // signing/building the response. If the create fails (non-201) or the durable
135
+ // object is unreachable, we suppress the challenge and fall through (return null)
136
+ // to the original MCP `authorization_required` error rather than advertise a
137
+ // consent/pickup URI for an anchor that was never persisted (clients would poll a
138
+ // §13 404/invalid-session forever).
139
+ const anchorStub = deps.getAnchorStub(env, sid);
140
+ let anchorPersisted = false;
141
+ try {
142
+ const createResponse = await anchorStub.fetch(`https://do${INTERNAL_PICKUP_CREATE}`, {
143
+ method: "POST",
144
+ headers: { "Content-Type": "application/json" },
145
+ body: JSON.stringify({
146
+ sid,
147
+ requiredScopes: verdict.requiredScopes,
148
+ ...(verdict.minAssurance !== undefined
149
+ ? { minAssurance: verdict.minAssurance }
150
+ : {}),
151
+ pollIntervalS: cfg.pollIntervalS,
152
+ anchorTtlS: cfg.anchorTtlS,
153
+ delegationExpiresInS: cfg.delegationExpiresInS,
154
+ }),
155
+ });
156
+ // reduceCreate returns 201 for both a fresh anchor and the idempotent reuse of a
157
+ // still-live one (W1 deterministic sid), so 201 is the only success status.
158
+ anchorPersisted = createResponse.status === 201;
159
+ if (!anchorPersisted) {
160
+ console.warn(`[kya-http] pickup anchor create returned ${createResponse.status} (sid=${sid}); suppressing §8.1 challenge`);
161
+ }
162
+ }
163
+ catch (error) {
164
+ console.warn(`[kya-http] pickup anchor create failed (sid=${sid}); suppressing §8.1 challenge:`, error instanceof Error ? error.message : String(error));
165
+ anchorPersisted = false;
166
+ }
167
+ if (!anchorPersisted) {
168
+ return null;
169
+ }
170
+ try {
171
+ const challengeProof = await signChallengeProof(await deps.proofGenerator(), {
172
+ sid,
173
+ consent_uri: buildConsentUri(verdict),
174
+ required_scopes: verdict.requiredScopes,
175
+ });
176
+ // Cooperative fetchers get the body-readable 200; everyone else the spec 401.
177
+ return wantsBodyReadable200
178
+ ? buildFetcherChallengeResponse(verdict, challengeProof)
179
+ : buildChallengeResponse(verdict, challengeProof);
180
+ }
181
+ catch (error) {
182
+ // The anchor persisted, but signing/building the challenge failed (e.g. a proof
183
+ // generator outage). Suppress the 401 and fall through to the original MCP error
184
+ // rather than throwing into a bare 500 — symmetric with the create-failure path
185
+ // above. The orphaned anchor is harmless: it expires via its ≤600s TTL.
186
+ console.warn(`[kya-http] challenge proof signing failed (sid=${sid}); suppressing §8.1 challenge:`, error instanceof Error ? error.message : String(error));
187
+ return null;
188
+ }
189
+ }
190
+ //# sourceMappingURL=challenge-middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"challenge-middleware.js","sourceRoot":"","sources":["../../src/delegation-http/challenge-middleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * draft-kya-http-02 delegation layer — §8.1 challenge emitter (builders + signing).
3
+ *
4
+ * Byte-exact against the vendored golden fixture
5
+ * `src/__tests__/fixtures/kya-http/challenge.delegation.full.json`. The wire format
6
+ * is frozen: param order is `CHALLENGE_PARAM_ORDER`, the separator is `", "`, every
7
+ * value is a quoted string, `scope` is space-joined, and `min_assurance` is omitted
8
+ * ENTIRELY when absent (the whole `min_assurance="…"` token is dropped, not emitted
9
+ * empty).
10
+ *
11
+ * The worker is its own Tier-2 authorization host: `consent_uri`/`pickup_uri` point
12
+ * at `authorizationHost`, while the Bearer challenge's `resource_metadata` points at
13
+ * the `realm` (the protected resource origin).
14
+ *
15
+ * @module @kya-os/mcp-i-cloudflare/delegation-http/challenge
16
+ */
17
+ import type { ChallengeVerdictInput } from "./types";
18
+ import type { CloudflareProofGenerator } from "../proof-generator";
19
+ /**
20
+ * Build the `WWW-Authenticate: Delegation …` header value (§8.1), byte-exact.
21
+ *
22
+ * Params are emitted in `CHALLENGE_PARAM_ORDER`, joined by `", "`, every value
23
+ * quoted. `min_assurance` is dropped entirely when `i.minAssurance` is absent.
24
+ */
25
+ export declare function buildWWWAuthenticateDelegation(i: ChallengeVerdictInput): string;
26
+ /**
27
+ * Build the companion `WWW-Authenticate: Bearer …` header value. `resource_metadata`
28
+ * points at the protected resource (`realm`), not the authorization host.
29
+ */
30
+ export declare function buildWWWAuthenticateBearer(realm: string): string;
31
+ /** The consent surface URL on the worker's authorization host: `…/consent?sid=<sid>`. */
32
+ export declare function buildConsentUri(i: ChallengeVerdictInput): string;
33
+ /** The pickup endpoint URL on the worker's authorization host: `…/api/delegation/pickup/<sid>`. */
34
+ export declare function buildPickupUri(i: ChallengeVerdictInput): string;
35
+ /**
36
+ * Build the human-facing `instructions` string (§8.1). Carries the four REQUIRED
37
+ * elements: (1) consent-uri relay, (2) pickup-uri + interval, (3) pinning
38
+ * requirement, (4) retry presentation. Byte-exact vs the fixture given its inputs.
39
+ */
40
+ export declare function buildInstructions(i: ChallengeVerdictInput): string;
41
+ /**
42
+ * Build the §8.1 401 JSON body. Key order mirrors the golden fixture's `http.body`.
43
+ * `min_assurance` is omitted when absent.
44
+ */
45
+ export declare function buildChallengeBody(i: ChallengeVerdictInput, challengeProof: string): object;
46
+ /**
47
+ * Build the full §8.1 401 `Response`: dual `WWW-Authenticate` challenges (Delegation
48
+ * first, then Bearer), `Retry-After`, JSON content type, and `Cache-Control:
49
+ * no-store`.
50
+ */
51
+ export declare function buildChallengeResponse(i: ChallengeVerdictInput, challengeProof: string): Response;
52
+ /**
53
+ * Build the body-readable §8.1 **200** `Response` for AI-agent fetchers that drop 4xx
54
+ * bodies (draft-kya-http-02 fetcher-compatibility envelope). REUSES `buildChallengeBody`
55
+ * so the JSON body is BYTE-IDENTICAL to the 401 — the org-signed `challenge_proof` binds
56
+ * the body content, not the HTTP envelope, so the same proof verifies at 200 or 401.
57
+ *
58
+ * Differs from the 401 (`buildChallengeResponse`) only in the envelope:
59
+ * - status `200`, not `401`
60
+ * - NO `WWW-Authenticate` (a 200 carrying it is reinterpreted as a 401 by some fetchers,
61
+ * re-dropping the body)
62
+ * - NO `Retry-After` (cadence lives in the body `poll_interval_s` + instructions)
63
+ * - adds the consent hint headers `KYA-Auth-Required`/`KYA-Auth-Url`/`Link` and the
64
+ * `KYA-Action: challenge` discriminator.
65
+ *
66
+ * The authoritative consent target is the proof-covered body `consent_uri`; the
67
+ * `KYA-Auth-Url`/`Link` headers are UNSIGNED hints for fetchers that only surface
68
+ * headers. Selection is a cooperative-UX bridge, NOT an access control.
69
+ */
70
+ export declare function buildFetcherChallengeResponse(i: ChallengeVerdictInput, challengeProof: string): Response;
71
+ /**
72
+ * Sign the `challenge_proof`: a verifiable 3-segment compact JWS whose `requestHash`
73
+ * commits to `{ sid, consent_uri, required_scopes }`.
74
+ *
75
+ * Implemented over the public `CloudflareProofGenerator.generateProof` seam: the
76
+ * challenge fields are canonicalized into the proof's request, so the signed
77
+ * `requestHash` cryptographically covers them. A verifier recomputes
78
+ * `sha256(canonical({ method: "kyaos/challenge", params }))` and checks it equals the
79
+ * payload's `requestHash`, then verifies the Ed25519 signature.
80
+ */
81
+ export declare function signChallengeProof(gen: CloudflareProofGenerator, payload: {
82
+ sid: string;
83
+ consent_uri: string;
84
+ required_scopes: string[];
85
+ }): Promise<string>;
86
+ //# sourceMappingURL=challenge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"challenge.d.ts","sourceRoot":"","sources":["../../src/delegation-http/challenge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAUnE;;;;;GAKG;AACH,wBAAgB,8BAA8B,CAC5C,CAAC,EAAE,qBAAqB,GACvB,MAAM,CAqBR;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,yFAAyF;AACzF,wBAAgB,eAAe,CAAC,CAAC,EAAE,qBAAqB,GAAG,MAAM,CAEhE;AAED,mGAAmG;AACnG,wBAAgB,cAAc,CAAC,CAAC,EAAE,qBAAqB,GAAG,MAAM,CAE/D;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,qBAAqB,GAAG,MAAM,CAUlE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,CAAC,EAAE,qBAAqB,EACxB,cAAc,EAAE,MAAM,GACrB,MAAM,CAkBR;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,CAAC,EAAE,qBAAqB,EACxB,cAAc,EAAE,MAAM,GACrB,QAAQ,CAYV;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,6BAA6B,CAC3C,CAAC,EAAE,qBAAqB,EACxB,cAAc,EAAE,MAAM,GACrB,QAAQ,CAcV;AAED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,wBAAwB,EAC7B,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,EAAE,CAAA;CAAE,GACvE,OAAO,CAAC,MAAM,CAAC,CAmBjB"}
@@ -0,0 +1,173 @@
1
+ /**
2
+ * draft-kya-http-02 delegation layer — §8.1 challenge emitter (builders + signing).
3
+ *
4
+ * Byte-exact against the vendored golden fixture
5
+ * `src/__tests__/fixtures/kya-http/challenge.delegation.full.json`. The wire format
6
+ * is frozen: param order is `CHALLENGE_PARAM_ORDER`, the separator is `", "`, every
7
+ * value is a quoted string, `scope` is space-joined, and `min_assurance` is omitted
8
+ * ENTIRELY when absent (the whole `min_assurance="…"` token is dropped, not emitted
9
+ * empty).
10
+ *
11
+ * The worker is its own Tier-2 authorization host: `consent_uri`/`pickup_uri` point
12
+ * at `authorizationHost`, while the Bearer challenge's `resource_metadata` points at
13
+ * the `realm` (the protected resource origin).
14
+ *
15
+ * @module @kya-os/mcp-i-cloudflare/delegation-http/challenge
16
+ */
17
+ import { CHALLENGE_PARAM_ORDER, KYAOS_ERRORS } from "./constants";
18
+ /**
19
+ * The JSON-RPC method label committed inside the `challenge_proof` JWS. The proof's
20
+ * `requestHash` is `sha256(canonical({ method, params: { sid, consent_uri,
21
+ * required_scopes } }))`, so the signed proof cryptographically covers the three
22
+ * challenge fields. Verifiers recompute this hash to confirm coverage.
23
+ */
24
+ const CHALLENGE_PROOF_METHOD = "kyaos/challenge";
25
+ /**
26
+ * Build the `WWW-Authenticate: Delegation …` header value (§8.1), byte-exact.
27
+ *
28
+ * Params are emitted in `CHALLENGE_PARAM_ORDER`, joined by `", "`, every value
29
+ * quoted. `min_assurance` is dropped entirely when `i.minAssurance` is absent.
30
+ */
31
+ export function buildWWWAuthenticateDelegation(i) {
32
+ const values = {
33
+ realm: i.realm,
34
+ sid: i.sid,
35
+ consent_uri: buildConsentUri(i),
36
+ pickup_uri: buildPickupUri(i),
37
+ scope: i.requiredScopes.join(" "),
38
+ min_assurance: i.minAssurance,
39
+ interval: String(i.pollIntervalS),
40
+ kind: "delegation",
41
+ };
42
+ const params = CHALLENGE_PARAM_ORDER.filter(
43
+ // min_assurance is the only optional param: omit the whole token when absent.
44
+ (key) => values[key] !== undefined).map((key) => `${key}="${values[key]}"`);
45
+ return `Delegation ${params.join(", ")}`;
46
+ }
47
+ /**
48
+ * Build the companion `WWW-Authenticate: Bearer …` header value. `resource_metadata`
49
+ * points at the protected resource (`realm`), not the authorization host.
50
+ */
51
+ export function buildWWWAuthenticateBearer(realm) {
52
+ return `Bearer resource_metadata="https://${realm}/.well-known/oauth-protected-resource"`;
53
+ }
54
+ /** The consent surface URL on the worker's authorization host: `…/consent?sid=<sid>`. */
55
+ export function buildConsentUri(i) {
56
+ return `https://${i.authorizationHost}/consent?sid=${i.sid}`;
57
+ }
58
+ /** The pickup endpoint URL on the worker's authorization host: `…/api/delegation/pickup/<sid>`. */
59
+ export function buildPickupUri(i) {
60
+ return `https://${i.authorizationHost}/api/delegation/pickup/${i.sid}`;
61
+ }
62
+ /**
63
+ * Build the human-facing `instructions` string (§8.1). Carries the four REQUIRED
64
+ * elements: (1) consent-uri relay, (2) pickup-uri + interval, (3) pinning
65
+ * requirement, (4) retry presentation. Byte-exact vs the fixture given its inputs.
66
+ */
67
+ export function buildInstructions(i) {
68
+ const consentUri = buildConsentUri(i);
69
+ const pickupUri = buildPickupUri(i);
70
+ return (`This action requires human authorization. Show your user this link and ask them to approve: ${consentUri}. ` +
71
+ `Then poll ${pickupUri} every ${i.pollIntervalS} seconds. ` +
72
+ `If you can sign HTTP requests, sign each poll with an ephemeral did:key; ` +
73
+ `otherwise generate one random 32-byte token and send it as X-KYA-Pickup-Token on EVERY poll. ` +
74
+ `When the response status is 200, take the 'delegation' value and retry your original request with header 'Authorization: Bearer <delegation>'.`);
75
+ }
76
+ /**
77
+ * Build the §8.1 401 JSON body. Key order mirrors the golden fixture's `http.body`.
78
+ * `min_assurance` is omitted when absent.
79
+ */
80
+ export function buildChallengeBody(i, challengeProof) {
81
+ const kyaos = {
82
+ sid: i.sid,
83
+ consent_uri: buildConsentUri(i),
84
+ pickup_uri: buildPickupUri(i),
85
+ required_scopes: i.requiredScopes,
86
+ };
87
+ if (i.minAssurance !== undefined) {
88
+ kyaos.min_assurance = i.minAssurance;
89
+ }
90
+ kyaos.poll_interval_s = i.pollIntervalS;
91
+ kyaos.challenge_proof = challengeProof;
92
+ return {
93
+ error: KYAOS_ERRORS.authorizationRequired,
94
+ kyaos,
95
+ instructions: buildInstructions(i),
96
+ };
97
+ }
98
+ /**
99
+ * Build the full §8.1 401 `Response`: dual `WWW-Authenticate` challenges (Delegation
100
+ * first, then Bearer), `Retry-After`, JSON content type, and `Cache-Control:
101
+ * no-store`.
102
+ */
103
+ export function buildChallengeResponse(i, challengeProof) {
104
+ const headers = new Headers();
105
+ headers.append("WWW-Authenticate", buildWWWAuthenticateDelegation(i));
106
+ headers.append("WWW-Authenticate", buildWWWAuthenticateBearer(i.realm));
107
+ headers.set("Retry-After", String(i.pollIntervalS));
108
+ headers.set("Content-Type", "application/json");
109
+ headers.set("Cache-Control", "no-store");
110
+ return new Response(JSON.stringify(buildChallengeBody(i, challengeProof)), {
111
+ status: 401,
112
+ headers,
113
+ });
114
+ }
115
+ /**
116
+ * Build the body-readable §8.1 **200** `Response` for AI-agent fetchers that drop 4xx
117
+ * bodies (draft-kya-http-02 fetcher-compatibility envelope). REUSES `buildChallengeBody`
118
+ * so the JSON body is BYTE-IDENTICAL to the 401 — the org-signed `challenge_proof` binds
119
+ * the body content, not the HTTP envelope, so the same proof verifies at 200 or 401.
120
+ *
121
+ * Differs from the 401 (`buildChallengeResponse`) only in the envelope:
122
+ * - status `200`, not `401`
123
+ * - NO `WWW-Authenticate` (a 200 carrying it is reinterpreted as a 401 by some fetchers,
124
+ * re-dropping the body)
125
+ * - NO `Retry-After` (cadence lives in the body `poll_interval_s` + instructions)
126
+ * - adds the consent hint headers `KYA-Auth-Required`/`KYA-Auth-Url`/`Link` and the
127
+ * `KYA-Action: challenge` discriminator.
128
+ *
129
+ * The authoritative consent target is the proof-covered body `consent_uri`; the
130
+ * `KYA-Auth-Url`/`Link` headers are UNSIGNED hints for fetchers that only surface
131
+ * headers. Selection is a cooperative-UX bridge, NOT an access control.
132
+ */
133
+ export function buildFetcherChallengeResponse(i, challengeProof) {
134
+ const consentUri = buildConsentUri(i);
135
+ const headers = new Headers();
136
+ headers.set("Content-Type", "application/json");
137
+ headers.set("Cache-Control", "no-store");
138
+ headers.set("KYA-Auth-Required", "true");
139
+ headers.set("KYA-Auth-Url", consentUri);
140
+ headers.set("Link", `<${consentUri}>; rel="kya-authorize"`);
141
+ headers.set("KYA-Action", "challenge");
142
+ return new Response(JSON.stringify(buildChallengeBody(i, challengeProof)), {
143
+ status: 200,
144
+ headers,
145
+ });
146
+ }
147
+ /**
148
+ * Sign the `challenge_proof`: a verifiable 3-segment compact JWS whose `requestHash`
149
+ * commits to `{ sid, consent_uri, required_scopes }`.
150
+ *
151
+ * Implemented over the public `CloudflareProofGenerator.generateProof` seam: the
152
+ * challenge fields are canonicalized into the proof's request, so the signed
153
+ * `requestHash` cryptographically covers them. A verifier recomputes
154
+ * `sha256(canonical({ method: "kyaos/challenge", params }))` and checks it equals the
155
+ * payload's `requestHash`, then verifies the Ed25519 signature.
156
+ */
157
+ export async function signChallengeProof(gen, payload) {
158
+ const proof = await gen.generateProof({
159
+ method: CHALLENGE_PROOF_METHOD,
160
+ params: {
161
+ sid: payload.sid,
162
+ consent_uri: payload.consent_uri,
163
+ required_scopes: payload.required_scopes,
164
+ },
165
+ }, { data: { sid: payload.sid } }, {
166
+ // The session binds the proof to this anchor; sid is the natural nonce.
167
+ nonce: payload.sid,
168
+ audience: payload.consent_uri,
169
+ sessionId: payload.sid,
170
+ });
171
+ return proof.jws;
172
+ }
173
+ //# sourceMappingURL=challenge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"challenge.js","sourceRoot":"","sources":["../../src/delegation-http/challenge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * draft-kya-http-02 delegation layer — consent → pickup bridge.
3
+ *
4
+ * The worker is its own Tier-2 authorization host: the §8.1 challenge points the
5
+ * caller's user at `consent_uri = /consent?sid=kya-…`, served here. This module is
6
+ * the route-order interception that `register.ts` wires BEFORE the legacy `/consent`
7
+ * routes; it only ever handles requests whose query carries an anchor sid (the
8
+ * `next()` fall-through for everything else lives in `register.ts`), so the legacy
9
+ * consent service is untouched.
10
+ *
11
+ * - `GET /consent?sid=kya-…` reads the anchor via `INTERNAL_PICKUP_INFO`. An unknown
12
+ * sid → 404 page; an expired sid → a "link expired" page; otherwise a minimal
13
+ * approve form (the consent-only scope list + a CSRF-protected POST).
14
+ * - `POST /consent/approve?sid=kya-…` mints a delegation VC-JWT using the SAME
15
+ * `@kya-os/mcp` primitives `consent.service.ts` uses (`wrapDelegationAsVC` →
16
+ * `createUnsignedVCJWT` → sign → `completeVCJWT`) — calling them directly here, NOT
17
+ * editing the service — then parks it on the anchor via `INTERNAL_PICKUP_APPROVE`.
18
+ * If the anchor is gone (DO 410/404) the approval FAILS with an error page and
19
+ * mints nothing reachable: consent on an EXPIRED anchor MUST fail.
20
+ *
21
+ * @module @kya-os/mcp-i-cloudflare/delegation-http/consent-anchor-bridge
22
+ */
23
+ import type { HonoContext } from "../runtime/oauth-handler";
24
+ import type { CloudflareEnv } from "../types";
25
+ import type { HttpChallengeConfig } from "../config";
26
+ /**
27
+ * Render the anchor consent surface for `GET /consent?sid=kya-…`.
28
+ */
29
+ export declare function handleAnchorConsentGet(c: HonoContext, sid: string, env: CloudflareEnv, _cfg: Required<HttpChallengeConfig>): Promise<Response>;
30
+ /**
31
+ * Handle anchor approval for `POST /consent/approve?sid=kya-…`. Mints a VC-JWT and
32
+ * parks it on the anchor; an expired/unknown anchor makes the approval FAIL.
33
+ */
34
+ export declare function handleAnchorConsentApprove(c: HonoContext, sid: string, env: CloudflareEnv, cfg: Required<HttpChallengeConfig>): Promise<Response>;
35
+ //# sourceMappingURL=consent-anchor-bridge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consent-anchor-bridge.d.ts","sourceRoot":"","sources":["../../src/delegation-http/consent-anchor-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAWH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAkWrD;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,CAAC,EAAE,WAAW,EACd,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,QAAQ,CAAC,mBAAmB,CAAC,GAClC,OAAO,CAAC,QAAQ,CAAC,CAiBnB;AAED;;;GAGG;AACH,wBAAsB,0BAA0B,CAC9C,CAAC,EAAE,WAAW,EACd,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,aAAa,EAClB,GAAG,EAAE,QAAQ,CAAC,mBAAmB,CAAC,GACjC,OAAO,CAAC,QAAQ,CAAC,CAyEnB"}