Proof of “Possession”: A DPoP Séance 🎃
Pull up a chair, dear reader, and join hands around the table. Outside, the wind is howling through the load balancers. Inside the parlor, a single candle flickers beside an old wooden Ouija board, and someone has left a bearer token lying on the mahogany table.
Beware, for a bearer token is a reckless talisman! Anyone who picks it up and whispers its inscription commands the spirits beyond the veil. The spirits never ask how the caller came to hold it. Earned in a midnight ritual or fished out of a dusty proxy log, it makes no difference. The underworld obeys whoever holds it in their hand.
But we who know the ancient incantations of RFC 9449, the dark art of Demonstrating Proof of Possession (DPoP), do not leave unbound talismans lying around the parlor. In our circle, the authorization server binds every token to a single medium's cryptographic key pair. When the medium calls into the beyond, the spirits check whose hand is moving the planchette before they answer.
Do not break the circle. The séance is about to begin.

Act I: The summoning
At the stroke of midnight, our medium (the client application) sits down at the Ouija board and conjures an asymmetric key pair in secret. It will never share the private key with a living soul. Instead, it rests its hands on the planchette to trace a DPoP proof, sealing the message with its private key and inscribing its public key for the spirits to judge.
THE CLIENT (THE MEDIUM)
(lighting a black candle, resting fingertips on the planchette)
"O Authorization Server, hear my call! I seek a talisman bound to my hand. Witness this proof, signed with my private key, with my public key inscribed upon it."
Http
POST /connect/token
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7...
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=9f8a7d...Thunder rattles the windowpanes. A cold draft sweeps across the board, the candle flame turns spectral green, and the Gatekeeper Spirit speaks from beyond the firewall.
THE AUTHORIZATION SERVER (THE GATEKEEPER SPIRIT)
"Your mark is true, medium, for the seal matches the public key you brought before me. Receive this access token. I have burned your key's thumbprint into its cnf.jkt claim, and the spirits will answer to your hand alone."
Act II: Calling the spirits of the vault
THE CLIENT (THE MEDIUM)
(turning to the API, hands on the planchette)
"Spirits of /api/grimoire, hear my call! I bear the bound talisman, and I trace a fresh proof for GET /api/grimoire at this very stroke of the clock."
Http
GET /api/grimoire HTTP/1.1
Host: haunt.example.com
Authorization: DPoP eyJhbGciOiJSUzI1NiIsImtpZCI6...
DPoP: eyJ0eXAiOiJkcG9wK2p3dCIsImFsZyI6IkVTMjU2...THE API (THE SPIRIT OF THE VAULT)
(inspecting the proof's claims from the other side of the veil)
"Let us see who calls. The method (htm) is GET, and the target URI (htu) is my altar. The timestamp (iat) is still warm. The token hash (ath) matches the talisman on the table, and the public key in your proof matches cnf.jkt. The veil parts for you, medium."
Act III: The charlatan at the board
Down in the cellar, a charlatan lurking inside a compromised reverse proxy fishes a copy of the access token out of a log file, creeps up to the parlor table, and grabs the planchette.
THE ATTACKER (THE CHARLATAN)
(waving the stolen access token)
"O Spirits of /api/grimoire, hear my call! I possess the talisman! Hand over your secrets!"
THE API (THE SPIRIT OF THE VAULT)
(unmoved)
"Silence, impostor! That talisman is bound to key thumbprint NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs. Move the planchette for this request with the matching private key, or begone from this table."
THE ATTACKER (THE CHARLATAN)
(checking its pockets, finding only the stolen token)
"Well, you see, I left my private key in my other cloak..."
The planchette does not budge.
Yet beware a fresher theft! If the charlatan snatches a proof while its ink is still wet and echoes the exact same plea, only a spirit that remembers every spoken jti will recognize the replay and bar the door.
Leaving the parlor with the lights on
Running a séance by hand takes a lot of ritual accuracy, and getting a single claim or status code wrong will haunt your production logs. Rather than rolling your own cryptography by candlelight, you can let our libraries handle each seat at the table:
- Duende.AccessTokenManagement equips your client as the medium, generating keys and signing proofs for every request.
- Duende IdentityServer acts as the Gatekeeper Spirit at the authorization server, validating proofs and binding tokens to client keys.
- Duende.AspNetCore.Authentication.JwtBearer.Extensions guards your .NET APIs on the other side of the veil, validating DPoP proofs and
cnfthumbprints without custom middleware.
If you want the daylight version of how all the pieces fit together, read Security Lingo Explained: DPoP and our walkthrough on DPoP Security for .NET APIs with JwtBearer Extensions. Lock your tokens to their keys before you blow out the candles. Have a safe Halloween.