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]