Skip to main content
Perflo sender-constrains every access and refresh token with Demonstrating Proof of Possession (DPoP). Keep one ES256 key for the grant, prove possession at the token endpoint, and use that key for every protected request.

Create and retain the grant key

Generate an ES256 key on the P-256 curve with a standards-based JavaScript Object Signing and Encryption (JOSE) or Web Cryptography application programming interface (API). Keep the private key inside the client’s secure storage and calculate the RFC 7638 thumbprint from its public JSON Web Key (JWK). Authorization-code clients send that thumbprint as dpop_jkt. Device clients establish the binding when they first exchange the device code. Refresh requests must use the original key, so losing it requires authorization again. Never send the private JWK to Perflo. Do not write the private key, DPoP proof, authorization code, access token, or refresh token to logs or fixtures.

Build a proof for the target endpoint

Each DPoP proof is a signed JSON Web Token (JWT) with an embedded public JWK. Build it for the exact discovered endpoint address and HTTP method. The proof header contains:
  • typ set to dpop+jwt
  • alg set to ES256
  • jwk containing only the public key
The proof payload contains:
  • jti with a unique value for this proof
  • htm with the uppercase HTTP method
  • htu with the normalized target URI, excluding query and fragment
  • iat with the current issue time
  • nonce when Perflo requires the current server nonce
  • ath with the access-token hash for a protected API request
Use the current issue time and a new jti for every proof. Perflo rejects stale proofs and reused identifiers. Authorization-server and protected-resource nonces are separate, so store them by endpoint role and replace a nonce whenever that endpoint returns a new one.

Send the proof at the token endpoint

Send the proof in the DPoP header when exchanging an authorization code or device code and when refreshing a grant. Confidential clients also authenticate with private_key_jwt; public clients use token endpoint authentication none. If Perflo returns a DPoP-Nonce header and a standard nonce challenge, create a new proof with that nonce before retrying. Never reuse the challenged proof because its jti is no longer fresh. The token response uses token_type=DPoP. Treat every returned token as opaque, even when an access token has a JWT shape.

Send a protected API request

An operation protected by PerfloOAuth in the OpenAPI document requires both headers:
Create a new proof for every request. The proof’s htm and htu must match that request, and its ath must bind the exact access token. Do not use Authorization: Bearer, cookies, query parameters, or request bodies as API credentials. Cross-origin resource sharing does not replace token or DPoP validation.

Keep bindings stable through refresh

A refresh token inherits the grant’s environment resource and DPoP key. Omit resource to retain the original value, or send that same single value. A refresh request can keep or narrow the granted scope set. It cannot change the DPoP key, add a scope, switch resources, or extend a grant beyond its registered and consented ceiling. See Choose an OAuth client profile for grant requirements and Call Perflo from a browser for browser-origin rules.