DPoP
RFC 9449 sender-constrained tokens. Every Shark-issued agent token can be cryptographically bound to a specific keypair via the cnf.jkt claim. The holder must prove possession of the private key on every request by attaching a fresh DPoP proof JWT.
The SDK ships:
DPoPProver— keypair holder + proof emitterDPoPHTTPClient(Python) — drop-in HTTP helper that auto-attaches proofsSharkClient.fetch(TypeScript) — same idea,fetch-shaped
Generate or load a keypair
ECDSA P-256 only. RSA and Ed25519 not supported by the server (yet).
Emit a proof manually
A proof is a short-lived JWT bound to a specific HTTP method, URL, and (optionally) the access token via the ath claim.
Auto-DPoP HTTP helper (Python)
Handles Authorization: DPoP <token> and a fresh DPoP proof on every call. The proof is bound to the exact method + URL.
Auto-DPoP fetch (TypeScript)
When dpopProver is set, c.fetch automatically:
- Sets
Authorization: DPoP <accessToken> - Generates a fresh proof bound to the request method + URL
- Sets
DPoP: <proof>
Token binding — cnf.jkt
When you request a token via get_token_with_dpop (Python) or via /oauth/token with a DPoP header (TS), the server embeds the prover's JWK thumbprint as cnf.jkt in the token. The resource server validates two things on every request:
- The DPoP proof was signed by a key whose JWK thumbprint matches
cnf.jkt. - The proof's
htm/htumatch the actual HTTP method and URL.
A stolen token is useless without the matching private key.
Key rotation
If a private key may be compromised, rotate via the admin API:
After rotation, the agent must request fresh tokens with new_prover.
Browser caveat
The TypeScript DPoP path uses jose's exportPKCS8 / importPKCS8 for PEM round-trips, which depends on Node-only crypto APIs. Browser builds work for everything except DPoP key PEM persistence — which most browser-resident agents don't need (they regenerate keys per session).
The proof emission itself (createProof) works fine in browsers via Web Crypto.
See also
- Delegation and agents
- Token exchange — preserving DPoP binding across exchange
- Existing reference: get_token_with_dpop
- Existing reference: http_with_dpop