Hi Bhasker,

Your review is reflected in two drafts posted today:

KYAPay Token -03:
https://www.google.com/url?q=https://www.ietf.org/archive/id/draft-skyfire-oauth-kyapay-token-03.html&source=gmail&ust=1793767494566000&sa=E
Using KYAPay Tokens -02:
https://www.google.com/url?q=https://www.ietf.org/archive/id/draft-skyfire-oauth-using-kyapay-tokens-02.html&source=gmail&ust=1793767494566000&sa=E

Issue #8 maps each point to the pull request that addressed it:
https://www.google.com/url?q=https://github.com/skyfire-xyz/draft-skyfire-oauth-using-kyapay-tokens/issues/8&source=gmail&ust=1793767494566000&sa=E

Thank you for a careful review.

Ankit


On Sun, Nov 01, 2026 10:12 AM, Ankit Agarwal <[email protected]> wrote:

> 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]> 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]
>> To unsubscribe send an email to [email protected]
>>
>
_______________________________________________
OAuth mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to