DPoP on the server side
DPoP on the server side
tproto OAuth requires DPoP (proof-of-possession tokens). the client side is
well documented; these are the server-side mechanics, from
zds, a PDS implementation in zig
(src/atproto/oauth/dpop.zig).
nonces can be stateless
the server must issue and rotate DPoP nonces, but nothing requires storing
them. zds derives the nonce as
base64url(HMAC-SHA256(secret, big_endian(time / rotation_secs))) and accepts
the counter−1 / counter / counter+1 windows on verification (dpop.zig:199-230).
rotation and validation with zero storage; the only thing persisted is JTI
replay protection (recordDpopJti(jti, expires_at)).
the use_dpop_nonce dance needs both channels
when a proof arrives without (or with a stale) nonce, respond with the fresh
nonce in a DPoP-Nonce header and
WWW-Authenticate: DPoP error="use_dpop_nonce" (dpop.zig:30-45) — real
clients key their retry off the www-authenticate error code, and sending only
the header strands them.
proof verification, field by field
htm— matches the http methodhtu— matchespublicUrl + pathwith query and fragment stripped before comparison (dpop.zig:183-197); proxies that rewrite paths break this firstath— hash of the access token; must be present when a token is bound and absent when there is none (dpop.zig:127) — accepting a spuriousathis a spec violation that most test suites missjkt— the key thumbprint binding, computed per RFC 7638 with the canonical member order{crv, kty, x, y}(dpop.zig:169-180); any other member order produces a different thumbprint and every token check fails- the EC public key is reconstructed from the JWK's
x/ywith the parity byte taken fromy[31] & 1(dpop.zig:145-160)
sources
- zds —
src/atproto/oauth/dpop.zig - atproto OAuth spec
- RFC 9449 (DPoP), RFC 7638 (JWK thumbprint)
Did you enjoy this article?
Recommend it — Standard Reader surfaces well-loved writing to more readers across the network.