Skip to content

docs: require binding the interaction handle to the browser; fix refresh-token binding claim - #409

Merged
osanderson merged 1 commit into
mainfrom
docs/interaction-handle-binding
Sep 28, 2026
Merged

osanderson merged 1 commit into
mainfrom
docs/interaction-handle-binding

Conversation

@osanderson

Copy link
Copy Markdown
Collaborator

Summary

Two documentation fixes from an attacker-mindset review.

Consent CSRF guidance (Medium finding)

CompleteAuthorization accepts an InteractionHandle from whoever presents it. Nothing in the docs said to bind the handle to the browser that started the interaction, and the reference login UI (cmd/conformance-as/authorize.go) carries it in a hidden form field.

A real login UI built that way, which signs users in from an existing session cookie, is open to consent CSRF:

  1. a malicious registered client starts its own authorization;
  2. it puts its handle in a self-submitting form on its own page;
  3. a signed-in victim's browser approves it, and the client receives a code for the victim's account.

This is the authorization-server side of the login CSRF fixed for the client in #397. The library can't enforce the fix because the embedder owns the login UI, so the docs now state it explicitly:

  • server.InteractionHandle doc comment: the attack, and the requirement to keep the handle in an HttpOnly, Secure, SameSite cookie, read it back with ParseInteractionHandle, CSRF-protect the form, and never accept it from a form field or query parameter alone.
  • GETTING_STARTED step 5: the same guidance, and the pointer to the reference code no longer recommends its handle bridge.
  • cmd/conformance-as/authorize.go: marked as not a model for a production login UI, and why.

server/doc.go refresh-token binding (doc error)

It said, in two places, that a refresh token is bound to the DPoP key it was issued under and that RefreshAccessToken rejects a proof from another key. The code deliberately doesn't check that (server/refresh.go): every client is confidential, and RFC 9449 §5 binds a confidential client's refresh token to client authentication. One of the two sentences was introduced by #405.

Testing

Docs and comments only; go build ./... and go vet pass.

🤖 Generated with Claude Code

@codecov

codecov Bot commented Sep 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

…esh-token binding claim

InteractionHandle's docs and GETTING_STARTED never said to bind the
handle to the browser that started the interaction, and the reference
login UI in cmd/conformance-as carries it in a hidden form field.
CompleteAuthorization accepts a handle from whoever presents it, so a
login UI built that way that signs users in from an existing session
cookie is open to consent CSRF: a malicious client starts its own
authorization, puts its handle in a self-submitting form, and a
signed-in victim's browser approves it. The handle's doc comment and
GETTING_STARTED now require keeping it in an HttpOnly, Secure, SameSite
cookie and CSRF-protecting the form, and the reference code is marked
as not a model for this.

server/doc.go also said, in two places, that a refresh token is bound
to the DPoP key it was issued under and that RefreshAccessToken rejects
a proof from another key. It deliberately isn't: every client is
confidential and RFC 9449 §5 binds its refresh token to client
authentication instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@osanderson
osanderson force-pushed the docs/interaction-handle-binding branch from 511edd2 to 30f2ca8 Compare September 28, 2026 17:01
@sonarqubecloud

Copy link
Copy Markdown

@osanderson
osanderson merged commit 1491d94 into main Sep 28, 2026
10 checks passed
@osanderson
osanderson deleted the docs/interaction-handle-binding branch September 28, 2026 17:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant