Hi Ankit,
Thanks for calling this out. I left a comment on PR #59 about path-based
tenants, and here is a short version for the list.
Your note says a recipient that has not onboarded checks that tdm names the
Target's host. The text in the PR says it checks that tdm names the domain,
which means the registrable domain from the PSL. For Targets that keep tenants
on paths under one host, that difference matters. Our API is at
app.beel.es/api/v1/companies/{id}/..., and marketplaces with seller pages under
one host are in the same spot. As written, a token with tdm app.beel.es/api
passes for any intermediary in front of anything under beel.es.
It would help if step 6 said how a recipient that cares about host and path
compares them, for example an exact host match plus a path-prefix match on
segment boundaries. If the path is only informational for intermediaries,
saying so would let Targets know they have to check it themselves.
Carlos MartÃnez
BeeL.
> El 1 nov 2026, a las 19:12, Ankit Agarwal
> <[email protected]> escribió:
>
> Hi Bhasker,
>
> The review is being worked through in GitHub, in issue #8, which links each
> point to the pull request that addresses it:
> https://github.com/skyfire-xyz/draft-skyfire-oauth-using-kyapay-tokens/issues/8
>
> One change goes further than the review asked, so I'm calling it out here
> (S4,
> https://github.com/skyfire-xyz/draft-skyfire-oauth-kyapay-token/pull/59). In
> the KYAPay Token draft, `aud` now identifies the combination of identities
> the issuer holds for the Target, and `tsi`, which identifies the service
> under it, becomes REQUIRED. A recipient that has onboarded to the issuer
> checks `aud` and `tsi`; any other recipient checks `tdm`, which names the
> Target's host. Comments are welcome on the pull request or here.
>
> Ankit
>
>
> On Wed, Oct 21, 2026 06:21 PM, Bhasker Allam <[email protected]
> <mailto:[email protected]>> wrote:
>> Hi all,
>>
>> Below is a review of "Using KYAPay Tokens" [USING-KYAPAY],
>> draft-skyfire-oauth-using-kyapay-tokens-01. Claims such as hid, aid
>> and sti are defined in the KYAPay Token draft [KYAPAY-TOKEN].
>> Section numbers refer to [USING-KYAPAY] unless stated otherwise.
>>
>> Summary
>> =======
>>
>> Most of the comments below come down to three high-level issues.
>>
>> 1. The bearer token carries personal and payment data.
>>
>> The token is a signed JWT, not an encrypted one (Section 4.1). In
>> [KYAPAY-TOKEN], hid requires email and may carry name and phone, and
>> sti carries settlement details. Anyone who sees the header during
>> its lifetime can read the data and replay the token anywhere within
>> it's aud. Every hop that terminates TLS (CDN, WAF, bot manager,
>> origin) sees it. Sections 5.1 and 9.3 answer this with "MUST NOT log
>> or forward", which cannot be enforced or checked.
>>
>> 2. Admission without challenge.
>>
>> Section 6.3 says a bot manager "SHOULD admit the request ... rather
>> than subjecting it to bot-blocking challenges." Combined with point
>> 1, a captured or cheaply obtained token becomes an open gate at the
>> bot manager. Section 7.2 allows "progressive" verification that
>> starts with only an email check, so "verified human" can mean very
>> little. I don't have a good solution here, since the goal of the
>> draft is easy adoption using the existing OAuth and JWT framework.
>>
>> 3. Some things to clarify to avoid implementation divergence.
>>
>> a. How intermediaries learn the aud set. Section 6.1 says to check
>> that the token is "intended for this recipient", but an
>> intermediary is not the recipient; the Target is. How will the
>> intermediary know the audience set?
>>
>> b. Single-use vs. reusable. Section 8.3 asks for jti replay
>> detection, while Section 4.3 validates "once (per session)" and
>> mnr implies multiple requests.
>>
>> c. What the human authorized. Section 6.6 treats a valid token as
>> evidence of a legitimate session, but the token does not name an
>> account or an action. Is hid meant to equal the account? Nothing
>> in the draft says so. Again this goes back to proof of
>> possession.
>>
>> Security comments
>> =================
>>
>> S1. The token does not say which account it is for (Sections 6.6, 6.7,
>> 8.8)
>>
>> Issue. A valid token with hid = attacker@example can be presented while
>> logging into, or acting on, someone else's account. Section 6.6 says an
>> ATO protector "SHOULD treat a validated KYAPay token ... as evidence
>> that a session is a human-initiated agentic session." That is true, but
>> it can be misread as evidence that the session is legitimate for this
>> account. The authors have said there is no low-burden way to bind the
>> token to an account today, and I agree.
>>
>> Suggestion.
>>
>> - Add a normative statement to 6.6 and 6.7, for example: "A KYAPay
>> token attests the identity of the principal who authorized the agent.
>> It does not convey authority over any account at the Target. ATO
>> protectors and CIAM systems MUST NOT treat a validated token as
>> authorization to access an account unless the token's principal has
>> been linked to that account."
>>
>> - List account binding as an open item next to step-up (6.5) and
>> revocation (8.7).
>>
>> - One possible path that keeps intermediary burden low is a DPoP change
>> I proposed on this list [DPOP-PROXY], which lets intermediaries
>> validate both the token and the sender.
>>
>> S2. Admission without challenge should depend on assurance (Sections
>> 6.2, 6.3, 7.2)
>>
>> Issue. Section 6.3 says a bot manager "SHOULD admit the request ...
>> rather than subjecting it to bot-blocking challenges." Section 7.2
>> allows verification to begin with an email check. A bearer token
>> obtained with a throwaway email then bypasses bot defenses on login,
>> sign-up and checkout.
>>
>> Suggestion. Make the admission SHOULD conditional: "...SHOULD admit the
>> request when the assurance levels in the token (Section 6.2) meet the
>> Target's policy for the requested action." Add that per-hid, per-aid
>> and per-apd rate and abuse limits still apply, and that sensitive
>> actions MAY still be challenged.
>>
>> S3. Say the token is reusable, and drop jti replay detection (Sections
>> 4.1, 4.3, 8.3)
>>
>> Issue. The token is meant to be reused within its roughly 5-minute
>> lifetime. Section 4.3 validates it "once (per session)" and the payment
>> claim mnr allows multiple requests. Yet Section 8.3 says verifiers
>> "SHOULD use the jti claim to detect replay." That can't work in
>> practice:
>>
>> - A Target is usually fronted by many intermediary instances (PoPs,
>> proxy fleets). Sharing jti state among them within the latency budget
>> in 6.3 ("no per-request callout") is not realistic.
>>
>> - Even a single-use token can't be enforced. Section 3.4 has the bot
>> manager, fraud manager, ATO protector and CIAM system all validate
>> the same token on the same request. Each layer would see the others'
>> use as a replay.
>>
>> Suggestion.
>>
>> - State in 4.1 that the token is reusable within its lifetime and aud.
>>
>> - List in-window replay within the aud set as an accepted risk of the
>> bearer model.
>>
>> - Reword 8.3 so jti is used for correlation, audit and issuer feedback
>> (6.10), not replay detection.
>>
>> - Point to proof of possession (4.3) as the only real fix, subject to
>> S8 below.
>>
>> S4. Define how intermediaries get the aud set (Sections 5.3, 6.1
>> step 5)
>>
>> Issue. aud works like a store-restricted gift card: the issuer limits
>> which Targets can accept the token. An intermediary, though, is not the
>> audience. Section 6.1 step 5 ("intended for this recipient") gives it
>> no way to know which aud values are valid for the hosts it protects.
>> Multi-tenant intermediaries front many Targets. How aud relates to tdm
>> and tsi is also not stated.
>>
>> Suggestion.
>>
>> - Say that intermediaries obtain the acceptable aud (and tdm/tsi)
>> values per protected Target through configuration, for example as
>> part of each trusted issuer's configuration, and check the token
>> against that set.
>>
>> - Define the relationship between aud, tdm and tsi.
>>
>> - Say how an agent determines the aud for a given Target.
>>
>> S5. Check the issuer before fetching keys (Section 6.1 steps 3 and 4)
>>
>> Issue. Step 3 verifies the signature against the issuer's key set
>> before step 4 checks that the issuer is trusted. Keys are found at
>> iss + /.well-known/jwks.json, so a literal reading has verifiers
>> fetching keys from any URL an attacker puts in iss (server-side request
>> forgery, denial of service). Well-built verifiers already allowlist
>> issuers, but the text should say so.
>>
>> Suggestion. Swap steps 3 and 4. Add: "A verifier MUST NOT retrieve key
>> material for an issuer that is not in its configured set of trusted
>> issuers."
>>
>> S6. Algorithm rules and token count (Sections 5.1, 6.1, References)
>>
>> Issue. The draft sets no algorithm rules. JWT Best Current Practices
>> [RFC8725] is only an informative reference.
>>
>> Suggestion.
>>
>> - Make [RFC8725] a normative reference.
>>
>> - Require verifiers to use an algorithm allowlist tied to each issuer's
>> keys, and to reject "none" and symmetric algorithms.
>>
>> - In 5.1, let recipients reject a header with more than one token per
>> typ (at most 3 tokens). This caps verification cost per request.
>>
>> S7. Token leakage through the agent's runtime (Section 8)
>>
>> Issue. Tokens can leak through LLM transcripts, tool logs, and prompt
>> injection. Section 7.1 mentions keeping credentials from the LLM, but
>> Section 8 does not cover the threat. Proposed text is in the "Proposed
>> text: new Section 8.x" part at the end of this message.
>>
>> S8. The planned proof-of-possession path reintroduces the rewriting
>> problem (Section 4.3)
>>
>> The bearer design is right not to depend on end-to-end request signing
>> such as Web Bot Auth. Signing the request's components is heavy, and
>> requests are routinely changed on the way to the origin. URL rewriting
>> is common, and sites deliberately keep external and internal URLs
>> separate so they don't expose internal architecture. When the signed
>> request is what the client sent, every proxy that rewrites the request
>> breaks verification or has to be given special handling.
>>
>> Issue. In Section 4.3 the issuer binds the agent's public key into the
>> token (cnf), and the agent then signs each request, "a signature over
>> the request (method, path, body digest, timestamp/nonce) using
>> [RFC9421]", which is verified against cnf. The draft prefers this over
>> a detached, DPoP-style proof [RFC9449]. Because the signature covers
>> the path, the same rewriting fragility the bearer model avoids comes
>> back: an intermediary that rewrites the URL, or an origin that sees
>> only the internal path, can no longer verify it. Section 4.3 also notes
>> that stopping replay of the signature "requires either per-Target nonce
>> caches or binding the signature to the request content". That is the
>> same shared-state problem across distributed intermediaries described
>> in S3.
>>
>> Suggestion.
>>
>> - Make the rationale explicit in 4.2 and 4.3: why whole-request signing
>> is avoided, including the URL rewriting and internal/external URL
>> separation points above.
>>
>> - For the proof-of-possession path, say which components are covered,
>> and require a set that survives normal proxying. Alternatively, allow
>> verification at the first intermediary, before any rewrite.
>>
>> - Consider a DPoP-style proof designed for intermediaries as an
>> alternative. My DPoP proposal on this list [DPOP-PROXY] addresses
>> exactly this for URI-rewriting reverse proxies.
>>
>> Privacy and PCI comments
>> ========================
>>
>> Sending the JWT in the clear is a reasonable choice: it reuses existing
>> OAuth/JWT infrastructure and keeps integration simple. The comments
>> below keep that model. They change what goes in the token, not how it
>> is carried.
>>
>> P1. Replace personal data with a per-Target pseudonymous identifier
>> (Sections 9.1, 9.3; hid in [KYAPAY-TOKEN])
>>
>> Issue. hid requires email and may carry name and phone. Every
>> intermediary receives it (9.3), and the same email reaches every
>> Target, which lets sites track a person across all of them.
>> Intermediaries don't need this data: bot, fraud and ATO decisions (6.3
>> to 6.6) need a stable handle and assurance levels, not a name or email.
>>
>> Suggestion.
>>
>> - Carry an opaque, pseudonymous principal identifier that differs per
>> Target (pairwise, derived per aud), plus the verification and
>> assurance sub-claims.
>>
>> - A Target that needs the personal data (for example CIAM account
>> creation in 6.7) gets it from the issuer out of band, authenticated
>> as that Target, similar to the UserInfo endpoint in OpenID Connect
>> Core 1.0 [OIDC], Section 5.3.
>>
>> - Intermediaries can still rate-limit per principal, scope mitigations
>> (6.9) and send feedback (6.10) using the pseudonym.
>>
>> - Only the Target makes an issuer call, and only when it needs the
>> data, so the "no per-request callout" property in 6.3 holds for
>> intermediaries.
>>
>> P2. Keep payment data out of the token (Section 6.4; sti in
>> [KYAPAY-TOKEN])
>>
>> Issue. If sti can contain card-related data, every hop that sees the
>> header may fall under PCI rules. Intermediary vendors and CDNs are
>> unlikely to accept that.
>>
>> Suggestion. Apply the P1 pattern: sti carries a reference that the
>> Target redeems with the issuer or payment processor. Add: "A KYAPay
>> token MUST NOT contain primary account numbers or other cardholder
>> data." Amount, currency and limits (amt, cur, mnr) can stay, since
>> fraud managers use them (6.4).
>>
>> P3. Make aid.creation_ip optional ([KYAPAY-TOKEN])
>>
>> Issue. creation_ip is REQUIRED. It is personal data, and Section 6.3
>> already says network-origin claims are "a weak corroborating signal
>> only."
>>
>> Suggestion. Make it OPTIONAL.
>>
>> Protocol and compatibility comments
>> ===================================
>>
>> C1. Header grammar and Structured Fields disagree (Sections 5.1, 10.1)
>>
>> Section 5.1 defines its own ABNF, while 10.1 registers KYAPay-Token as
>> a Structured Field of type List. Under Structured Field Values for HTTP
>> [RFC9651], a malformed member makes the whole field fail to parse. That
>> conflicts with 5.1's "MUST ignore a member it cannot parse ... while
>> continuing to process the remaining members." Pick one: define the
>> field as an sf-list of sf-string (or sf-token) and drop the custom
>> ABNF, or drop the Structured Field registration.
>>
>> C2. Header size (Section 5.1)
>>
>> Up to three JWTs carrying identity and payment claims can exceed common
>> header limits (nginx header buffers, load balancer and CDN caps). Give
>> a recommended maximum field size, or at least a note. The P1 and P2
>> changes would shrink tokens considerably.
>>
>> C3. Browser agents and CORS (Sections 3.3, 5.1)
>>
>> A custom request header on a cross-origin fetch triggers a CORS
>> preflight. Page scripts can't send it unless the site allows it in
>> Access-Control-Allow-Headers. Browser-use agents also can't easily add
>> headers to page navigations. Say which requests carry the token
>> (top-level navigations, API calls, sub-resources), and qualify the
>> "zero merchant tech lift" claim for browser-based agents.
>>
>> C4. No challenge or error response (Sections 6.5, 8.8)
>>
>> A Target cannot signal "token required", "token rejected" or "higher
>> assurance needed." For step-up, which 6.5 lists as open, consider
>> reusing OAuth 2.0 Step Up Authentication Challenge Protocol [RFC9470],
>> which already defines WWW-Authenticate challenges for insufficient
>> authentication. It would also help to explain why the draft uses a new
>> header rather than an Authorization scheme (presumably to avoid
>> clashing with the site's own authentication).
>>
>> Proposed text: new Section 8.x, Token Exposure in the Agent Runtime
>> ===================================================================
>>
>> Suggested for insertion after Section 8.5 (Compromised Agent Host). It
>> complements 8.5: that section covers malware using the agent's key,
>> while this one covers the token leaving the agent through its own
>> normal operation.
>>
>> 8.x. Token Exposure in the Agent Runtime
>>
>> Agents driven by language models process untrusted content and
>> record their activity in ways that conventional HTTP clients do not.
>> A KYAPay token can therefore leave the agent through normal
>> operation, without the agent host being compromised. Examples
>> include:
>>
>> o the token appearing in model context, conversation transcripts,
>> or tool-call inputs and outputs that are logged, retained, or
>> sent to a model provider;
>>
>> o agent tracing, observability, or debugging systems that record
>> outbound request headers;
>>
>> o prompt injection, where content retrieved from a website or tool
>> instructs the agent to reveal the token or to include it in a
>> request to a party other than the intended Target.
>>
>> For a bearer token (Section 4.1), anyone who obtains a leaked token
>> can use it at any audience in its aud until it expires. Audience
>> binding does not help if the attacker presents the token at a
>> legitimate Target.
>>
>> Agent developers and KYAPay Token Issuers SHOULD mint and attach
>> tokens in deterministic components (Section 7.1) whose outputs are
>> not placed in model context. Agents SHOULD attach a token only to
>> requests whose destination matches the token's audience (aud, and
>> tdm or tsi where used), and the component that attaches it MUST
>> make that decision without relying on model output. Agent runtimes
>> MUST NOT record KYAPay token values in logs, traces, or
>> transcripts. Agents SHOULD request a new token for each Target and
>> task rather than reusing one broadly, and SHOULD request the
>> shortest lifetime that fits the task. Proof of possession (Section
>> 4.3) reduces this exposure only if the private key is also kept out
>> of model context.
>>
>> References
>> ==========
>>
>> [USING-KYAPAY]
>> Agarwal, A., Jones, M. B., and S. Thumma, "Using KYAPay Tokens",
>> draft-skyfire-oauth-using-kyapay-tokens-01,
>> https://datatracker.ietf.org/doc/draft-skyfire-oauth-using-kyapay-tokens/
>> Editor's copy:
>>
>> https://skyfire-xyz.github.io/draft-skyfire-oauth-using-kyapay-tokens/draft-skyfire-oauth-using-kyapay-tokens.html
>>
>> [KYAPAY-TOKEN]
>> Agarwal, A. and M. B. Jones, "KYAPay Token",
>> draft-skyfire-oauth-kyapay-token-01,
>> https://datatracker.ietf.org/doc/html/draft-skyfire-oauth-kyapay-token-01
>>
>> [DPOP-PROXY]
>> Allam, B., DPoP proposal for URI-rewriting reverse proxies, message
>> to the OAuth WG mailing list,
>> https://mailarchive.ietf.org/arch/msg/oauth/F-UUNUUuZJihx_Qpms6EGsItoyc/
>>
>> [RFC8725]
>> Sheffer, Y., Hardt, D., and M. Jones, "JSON Web Token Best Current
>> Practices", BCP 225, RFC 8725,
>> https://www.rfc-editor.org/rfc/rfc8725
>>
>> [RFC9421]
>> Backman, A., Ed., Richer, J., Ed., and M. Sporny, "HTTP Message
>> Signatures", RFC 9421,
>> https://www.rfc-editor.org/rfc/rfc9421
>>
>> [RFC9449]
>> Fett, D., Campbell, B., Bradley, J., Lodderstedt, T., Jones, M., and
>> D. Waite, "OAuth 2.0 Demonstrating Proof of Possession (DPoP)",
>> RFC 9449,
>> https://www.rfc-editor.org/rfc/rfc9449
>>
>> [RFC9470]
>> Bertocci, V. and B. Campbell, "OAuth 2.0 Step Up Authentication
>> Challenge Protocol", RFC 9470,
>> https://www.rfc-editor.org/rfc/rfc9470
>>
>> [RFC9651]
>> Nottingham, M. and P-H. Kamp, "Structured Field Values for HTTP",
>> RFC 9651,
>> https://www.rfc-editor.org/rfc/rfc9651
>>
>> [OIDC]
>> Sakimura, N., Bradley, J., Jones, M., de Medeiros, B., and C.
>> Mortimore, "OpenID Connect Core 1.0",
>> https://openid.net/specs/openid-connect-core-1_0.html
>>
>> Thanks,
>> Bhasker Allam
>> _______________________________________________
>> OAuth mailing list -- [email protected] <mailto:[email protected]>
>> To unsubscribe send an email to [email protected]
>> <mailto:[email protected]>
> _______________________________________________
> OAuth mailing list -- [email protected]
> To unsubscribe send an email to [email protected]
_______________________________________________
OAuth mailing list -- [email protected]
To unsubscribe send an email to [email protected]