Skip to content

Add the RFC 9449 ath claim to DPoP proofs accompanying an access token - #789

Open
josepharuja wants to merge 1 commit into
solid-contrib:mainfrom
josepharuja:add-rfc9449-ath-claim
Open

Add the RFC 9449 ath claim to DPoP proofs accompanying an access token#789
josepharuja wants to merge 1 commit into
solid-contrib:mainfrom
josepharuja:add-rfc9449-ath-claim

Conversation

@josepharuja

Copy link
Copy Markdown

Follows up #767 and #786, both of which describe this; posting as a PR with the change and its tests rather than a third issue.

What

RFC 9449 §4.3 step 12 requires a DPoP proof presented together with an access token to carry ath — the base64url-encoded SHA-256 hash of the token's ASCII value (§4.2) — and requires resource servers to verify it. Client.generateDpopToken currently sets jti, htm, htu and iat; this PR adds ath, guarded on the client holding an access token, so proofs made before a token exists (the token request itself) are unchanged.

  • JwsUtils.accessTokenHash(String) carries the hashing, with a test pinned to the worked example in RFC 9449 §4.3 (Kz~8mXK1EalYznwH-LC-1fBAo.4Ljp~zsPE_NeO.gxUfUHyO2r2Z3DZ53EsNrWBb0xWXoaNy59IiKCAqksmQEo).
  • ClientTest gains two cases: the claim is present on getAuthHeaders proofs and hashes the accompanying token; the claim is absent from token-less signRequest proofs.

Why it matters, with a measurement

A resource server that implements §4.3 as written must reject a proof with no ath, so every authenticated request the harness makes falls through to anonymous and the run stops in PREPARE SERVER. We ran the suite (1.2.2, test suite 0.0.19, 41 test cases) twice against the same server build — one that validates ath — changing only this guarded claim in the harness client:

Harness client MustFeatures MustScenarios
Unmodified 0 passed / 0 failed / 41 untested (halts in PREPARE SERVER)
With ath 24 passed / 11 failed 613 passed / 28 failed

The gap between the rows is the cost of the one absent claim: the difference between a §4.3-strict implementation receiving no signal at all and receiving an itemised list of real defects to fix. Servers that skip the ath check are unaffected by this change — the proof simply carries one additional claim they ignore.

Happy to adjust anything about the shape of the change — the hashing could live in Client instead of JwsUtils if you prefer it private, and the tests follow the existing mockClient pattern.

The harness is the reason a second implementation can measure itself at all — thank you for maintaining it.

josepharuja added a commit to enrichmeai/cistern that referenced this pull request Aug 28, 2026
… the provisional numbers beside it

Official row (2026-08-28, unmodified harness, main 75c0281): still 0/0/41,
verified by a fresh run — but for the first time the halt is not ours.
REGISTER CLIENTS passes; the run stops one step later because the harness
client's DPoP proofs carry no ath and RFC 9449 §4.3 obliges us to reject
exactly that (one ATH_MISSING in the server log per run). Upstream fix
offered: solid-contrib/conformance-test-harness#789, per the owner's ruling
to contribute rather than route around.

Beside it, clearly fenced as NOT the baseline: the patched-client figures
(24/11 MustFeatures, 613/28 MustScenarios) — the measured cost of the one
absent claim, and the itemised work queue (#165#170). The official row
moves only on an unmodified-harness run; that is the ratchet's whole value.

Rebuilt from a clean worktree: the first cut of this branch was committed
from a stale shared checkout and silently carried reverts of #159, #160,
#162 and #163 — caught by the pre-merge review, discarded, and the shared
checkout is being retired from write duty.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: josepharuja <joseph.a.aruja@gmail.com>
josepharuja added a commit to enrichmeai/cistern that referenced this pull request Aug 28, 2026
… the provisional numbers beside it (#171)

Official row (2026-08-28, unmodified harness, main 75c0281): still 0/0/41,
verified by a fresh run — but for the first time the halt is not ours.
REGISTER CLIENTS passes; the run stops one step later because the harness
client's DPoP proofs carry no ath and RFC 9449 §4.3 obliges us to reject
exactly that (one ATH_MISSING in the server log per run). Upstream fix
offered: solid-contrib/conformance-test-harness#789, per the owner's ruling
to contribute rather than route around.

Beside it, clearly fenced as NOT the baseline: the patched-client figures
(24/11 MustFeatures, 613/28 MustScenarios) — the measured cost of the one
absent claim, and the itemised work queue (#165#170). The official row
moves only on an unmodified-harness run; that is the ratchet's whole value.

Rebuilt from a clean worktree: the first cut of this branch was committed
from a stale shared checkout and silently carried reverts of #159, #160,
#162 and #163 — caught by the pre-merge review, discarded, and the shared
checkout is being retired from write duty.

Signed-off-by: josepharuja <joseph.a.aruja@gmail.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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>
@josepharuja
josepharuja force-pushed the add-rfc9449-ath-claim branch from 7e56052 to 12a3273 Compare August 28, 2026 02:40
josepharuja added a commit to enrichmeai/cistern that referenced this pull request Aug 28, 2026
…d outbound (#175)

Owner directive 2026-08-28, reversing 2026-07-17: no AI co-author trailers from the cutover onward; history unrewritten; outbound contributions carry no AI attribution anywhere (first applied on solid-contrib/conformance-test-harness#789).

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