> ## Documentation Index
> Fetch the complete documentation index at: https://docs.perflo-api.proofof.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Use DPoP with Perflo tokens

> Bind authorization grants and token requests to an ES256 proof-of-possession key.

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:

```http theme={null}
Authorization: DPoP your_access_token_here
DPoP: your_dpop_proof_here
```

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](/oauth/client-profiles) for grant requirements and [Call Perflo from a browser](/oauth/browser-access) for browser-origin rules.
