Skip to content

Add the RFC 9449 ath claim to DPoP proofs - #786

Open
JamesRadley wants to merge 1 commit into
solid-contrib:mainfrom
JamesRadley:fix/dpop-ath-claim
Open

JamesRadley wants to merge 1 commit into
solid-contrib:mainfrom
JamesRadley:fix/dpop-ath-claim

Conversation

@JamesRadley

Copy link
Copy Markdown

RFC 9449 §4.3 requires a DPoP proof to carry an ath claim — base64url(SHA-256(ASCII(access_token))) — whenever the proof accompanies an access token. It is what binds a proof to one specific token; without it a proof captured from one request can be presented alongside a different token issued to the same key.

The harness currently sets only jti, htm, htu and iat. Against a resource server that enforces the requirement, every authenticated request the harness makes is rejected before any test assertion runs, so the whole suite is unusable against that server. We hit this running the harness against our own Solid server, which enforces ath; the requests failed with 401 invalid_dpop_proof, and adding the claim was the only change needed to get the suite running.

What changed

Both proof sites that accompany an access token now bind to it:

  • getAuthHeaders already had the token to hand.
  • authorize sets the Authorization header itself and then delegates to signRequest, which builds the proof from a provisional request and so had no way to know the token. signRequest gains an overload that takes it. The existing single-argument signature is unchanged and still means "no access token accompanies this request", so this is source-compatible for any caller (including Karate features) using the public method.

requestAccessToken deliberately continues to send a proof with no ath: there is no access token at that point, and RFC 9449 only requires the claim when one is present.

The three claim names are lifted into HttpConstants alongside the existing DPoP constants.

Tests

Three cases added to ClientTest:

  • getAuthHeadersDpopBindsProofToAccessToken — the proof's ath matches the token in the Authorization header.
  • signRequestWithAccessTokenBindsProofToIt — the overload sets ath correctly.
  • signRequestWithoutAccessTokenOmitsAth — the no-token path still omits ath, and htm/htu are unchanged.

ClientTest passes in full (60 tests).

Happy to adjust naming or approach if you would prefer the token threaded differently.

RFC 9449 section 4.3 requires a DPoP proof to carry an `ath` claim — the
base64url encoding of the SHA-256 hash of the ASCII encoding of the access
token — whenever the proof accompanies an access token. The harness omitted it,
so a resource server that enforces the requirement rejects every authenticated
request the harness makes.

Both proof sites that accompany a token are fixed, not just the obvious one:

  - `getAuthHeaders` already had the token to hand.
  - `authorize` sets the Authorization header itself and then delegates to
    `signRequest`, which builds the proof from a provisional request and so had
    no way to know the token. `signRequest` gains an overload taking the token;
    the existing single-argument signature is unchanged and still means "no
    access token accompanies this request".

The token request in `requestAccessToken` correctly continues to send a proof
with no `ath`: there is no access token at that point.

The claim names are lifted into HttpConstants alongside the existing DPoP
constants.
josepharuja added a commit to josepharuja/conformance-test-harness that referenced this pull request Aug 28, 2026
RFC 9449 §4.3 step 12 requires a DPoP proof presented together with an access
token to carry ath — the base64url-encoded SHA-256 of the token's ASCII value
(§4.2) — and requires resource servers to verify it. Client.generateDpopToken
sets jti, htm, htu and iat; this adds ath, guarded so proofs made before a
token exists (the token request itself) are unchanged.

JwsUtils.accessTokenHash carries the hashing, tested against the worked
example in RFC 9449 §4.3. ClientTest verifies the claim is present on
getAuthHeaders' proofs and absent from token-less signRequest proofs.

Fixes solid-contrib#767. Fixes solid-contrib#786.

Signed-off-by: Joseph Antony Aruja <joseph.a.aruja@gmail.com>
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