---
schema_version: "1"
id: 66ea6fe0e06abe0884ea693a1ecf647ced25e2a54eb101d3ab0632b752157995
title: OAuth 2.0 DPoP Guide
collection: admin
version: current
locale: en-us
portal_url: https://docs.salesforce.com/admin/current/en-us/xcloud/264-0-0/remoteaccess_dpop_guide.md
canonical_url: https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_dpop_guide.htm&release=264.0.0&type=5
source_site: help.salesforce.com
content_type: documentation
content_hash: e9114c529ced6e775470123e2f8b8b26d81c874b45e68f87d87434a8161e7f3e
description: To secure your client apps, implement the Demonstrating-Proof-of-Possession (DPoP) extension for OAuth integrations. DPoP cryptographically binds OAuth access and refresh tokens to your client. To use a token, clients must prove that they own the private key. Even if a token is leaked or stolen, a threat actor can’t use it without the private key.
products:
  - Security
  - Cross Cloud
release: "264"
---
# OAuth 2.0 DPoP Guide

To secure your client apps, implement the Demonstrating-Proof-of-Possession (DPoP) extension for OAuth integrations. DPoP cryptographically binds OAuth access and refresh tokens to your client. To use a token, clients must prove that they own the private key. Even if a token is leaked or stolen, a threat actor can’t use it without the private key.

### Required Editions

<table class="slds-table slds-table_bordered slds-max-medium-table_stacked slds-m-bottom_small edition"><colgroup><col style="width:100%"></colgroup><tbody><tr class=""><td class="slds-cell-wrap" style="vertical-align:top;">Available in: all editions</td></tr></tbody></table>

DPoP is defined in [RFC 9449: OAuth 2.0 Demonstrating Proof of Possession (DPoP)](https://datatracker.ietf.org/doc/html/rfc9449). DPoP works by requiring the client to send proof-of-possession in every token and resource request. To do this, the client sends a special JSON Web Token (JWT) called a DPoP proof. The DPoP proof contains the public key, along with some information about the request. The client signs the DPoP proof with the private key. Salesforce can verify the signature and confirm that the DPoP proof isn’t fake or altered.

## DPoP Prerequisites

- An OAuth [external client app](https://help.salesforce.com/s/articleView?id=xcloud.configure_external_client_app_oauth_settings.htm&type=5&language=en_US) or [connected app](https://help.salesforce.com/s/articleView?id=xcloud.connected_app_create_api_integration.htm&type=5&language=en_US) with the “Require DPoP (Demonstrating Proof-of-Possession)” setting turned on
- A client certificate with a public-private key pair.
    - To easily get a certificate, [generate a self-signed certificate](https://help.salesforce.com/s/articleView?id=xcloud.security_keys_creating.htm&type=5&language=en_US).
    - Alternatively, your client can generate the certificate itself.

## Authorization Code Requests

Authorization code flows include the web-server flow, hybrid web-server flow, and headless identity flows. For these flows, DPoP protects the authorization code by binding it to the public key. This way, a threat actor can’t use a stolen authorization code and their own key to request an access token. To bind the authorization code, the authorization request must include a `dpop_jkt` parameter with an identifier or “thumbprint” for the public key.

### Compute the dpop\_jkt from the Public Key

To get the dpop\_jkt value, compute the JSON Web Key (JWK) thumbprint of the public key:

- Extract the public key from the client certificate.
- Construct the JWK, a JSON object that represents the public key. Include only the members required for the key's cryptographic algorithm. See [RFC 7638: JSON Web Key (JWK) Thumbprint](https://www.rfc-editor.org/rfc/rfc7638.html) for detailed descriptions of all members. Order the members alphabetically and remove all whitespaces and line breaks.
    - Rivest-Shamir-Adleman (RSA): e, kty, and n, in that order.
    - Elliptic Curve (EC): crv, kty, x, and y, in that order.
- Omit optional members, such as alg and kid, so that the thumbprint reflects the key itself.
- Serialize the JWK to its UTF-8 octets.
- Hash the octets with the SHA-256 algorithm, then base64url-encode the result. This value is the `dpop_jkt.`

### Request an Authorization Code

Request an authorization code from the Salesforce /services/oauth2/authorize endpoint. Include the dpop\_jkt parameter in the request. Here’s an example request for the web-server flow:

```
https://MyCompany.my.salesforce.com/services/oauth2/authorize?response_type=code&client_id=<client_id>&
redirect_uri=https://www.mycustomerorderstatus.com/oauth2/callback&dpop_jkt=<JWK thumbprint>
```

## DPoP Token Requests

In token requests, DPoP requires your client to send a DPoP proof JWT. To prevent attackers from using stolen requests (replay attacks), the token request must also include a valid nonce value. The nonce is a random value that is only valid for 24 hours. To get the nonce value, your client makes an initial request to the token endpoint, handles an expected error response that includes the nonce, and then retries.

Here’s the token request pattern:

- Your client generates a DPoP proof.
- Your client sends an initial request to the /services/oauth2/token endpoint. The request includes the DPoP proof.
- Salesforce returns an expected error response, which includes a `dpop_nonce` value. Your client handles the error by extracting the `dpop_nonce`.
- Your client generates a new DPoP proof that includes the nonce.
- Your client retries the token request with the new DPoP proof.
- Salesforce grants an access token and any other requested tokens.

### Generate an Initial DPoP Proof

The DPoP proof consists of a header and payload. The header contains the public key. The payload contains information about the request.

To create the DPoP proof, use the [`JWTUtil.generateDPoPProofForToken(httpMethod, httpUri, dpopCert, nonce, token)`](https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_class_Auth_JWTUtil.htm#apex_Auth_JWTUtil_generateDPoPProofForToken) Apex method. To use this Apex method, the client certificate must be saved in the Certificate and Key Management page in Setup.

Alternatively, you can develop your client app to generate the DPoP proof:

- Generate a header and payload with the required claims.
- Base64-encode the header and payload.
- Cryptographically sign the base64-encoded value with the private key.

For a detailed description of all claims in the DPoP proof, see the DPoP Proof JWTs section in [RFC 9449: OAuth 2.0 Demonstrating Proof of Possession (DPoP)](https://datatracker.ietf.org/doc/html/rfc9449#name-dpop-proof-jwts).

Here's an example header:

```
{
  "alg": "ES256",
  "jwk": {
    "crv": "P-256",
    "kty": "EC",
    "x": "vCAiGjuhIeMCTEJq_N9-oJRjPTRENezkoQ5TBaJtK7Y",
    "y": "YXWanpPri_6o2virYzsAfVNZhAW3SQaxFSBcs89BC-Q"
  },
  "typ": "dpop+jwt"
}
```

Here's an example payload:

```
{
  "jti": "EHFHE41EbRagW440IfHkUwVo",
  "htm": "POST",
  "htu": "https://MyCompany.my.salesforce.com/services/oauth2/token",
  "iat": 1787773196
}
```

### Send an Initial Request to the Token Endpoint

The initial request must include a DPoP header containing the DPoP proof. Here’s an example request for the web-server flow:

```
POST /services/oauth2/token HTTP/1.1
Host: MyCompany.my.salesforce.com
Content-type: application/x-www-form-urlencoded
Accept: application/json
DPoP: eyJ0eX...
grant_type=authorization_code&
code=aPrx5…&
client_id=<client_id>&
client_secret=<client_secret>&
redirect_uri=https://www.mycustomerorderstatus.com/services/oauth2/success
```

### Handle the DPoP Nonce Error

As expected, Salesforce returns a “400 Bad Request” error response. The response includes a `DPoP-Nonce` header that contains a nonce value. Your client must extract the value from the `DPoP-Nonce` header.

```
HTTP/1.1 400 Bad Request
 DPoP-Nonce: eyJ7S_z...
 {
  "error": "use_dpop_nonce",
  "error_description":
    "Authorization server requires nonce in DPoP proof"
 }
```

### Generate a DPoP Proof with the Nonce Value

Before retrying the token request, generate a new, unique DPoP proof. This time, include a nonce claim with the nonce value.

Because the nonce is valid for only 24 hours, your client-side code must keep it fresh. If the nonce has expired, the token request returns a `use_dpop_nonce` error with a new nonce. Make sure that your client-side code handles this error by extracting the new nonce.

```
{
  "jti": "YjMja9CviFSJLSG5tO_K5Gti",
  "htm": "POST",
  "htu": "https://MyCompany.my.salesforce.com/services/oauth2/token",
  "iat": 1787773196,
  "nonce": "eyJ7S_z..."
}
```

### Retry the Token Request

Now that you have the nonce embedded into a new DPoP proof, retry the token request.

```
POST /services/oauth2/token HTTP/1.1
Host: MyCompany.my.salesforce.com
Content-type: application/x-www-form-urlencoded
Accept: application/json
DPoP: <new DPoP proof with valid nonce>
grant_type=authorization_code&
code=aPrx5…&
client_id=<client_id>&
client_secret=<client_secret>&
redirect_uri=https://www.mycustomerorderstatus.com/services/oauth2/success
```

### Get a DPoP-Bound Access Token

If the request is successful, Salesforce returns a DPoP-bound access token. Refresh tokens, if requested, are also DPoP-bound. The `token_type` for this response is DPoP.

```
{
  "access_token": "00D...",
  "refresh_token": "5Ae...",
  "signature": "/5Z...",
  "scope": "refresh_token web id api",
  "instance_url": "https://MyCompany.my.salesforce.com",
"id":"https://login.salesforce.com/id/00DB0000000TfcRMAS/005B0000005Bk90IAC",
  "token_type": "DPoP",
  "issued_at": "1787761167317"
}
```

## Resource Requests

When you have a DPoP-bound access token, use it to request access to Salesforce resources.

### Generate a DPoP Proof with the Hashed Access Token

Your client computes the SHA-256 hash of the access token. It then generates a new DPoP proof with the hashed access token in the ath payload claim. The DPoP proof must also include a valid nonce value. Here’s an example payload:

```
{
  "jti": "uzUwriQThuMBtwG9CSQxHHMY",
  "htm": "POST",
  "htu": "https://MyCompany.my.salesforce.com/services/oauth2/userinfo",
  "iat": 1787773633,
  "ath": "LT0...",
  "nonce": "ni4..."
}
```

### Request Access to Salesforce Resources

To access Salesforce data, send a request to the resource endpoint with these headers:

- `Authorization: DPoP <DPoP-bound access token>`
- `DPoP: <DPoP proof JWT with hashed token in ath claim>`

Here’s an example request for user information:

```
POST /services/oauth2/userinfo HTTP/1.1
Host: MyCompany.my.salesforce.com
Authorization: DPoP 00DSG00000slUEr!AQEA...PmxpTx.hs
DPoP: <new DPoP proof with hashed access token & valid nonce>
Content-Type: application/x-www-form-urlencoded
Accept: application/json
```

### Use DPoP-Bound Refresh Tokens

To use a DPoP-bound refresh token, the request to the /services/oauth2/token endpoint must include a unique DPoP proof with a valid nonce value. If the nonce has expired, the call returns a use\_dpop\_nonce value with a new nonce.

```
POST /services/oauth2/token HTTP/1.1
Host: MyCompany.my.salesforce.com
DPoP: <new DPoP proof with valid nonce>
grant_type=refresh_token&
client_id=3MVG9lKcPoNINVBIPJjdw1J9LLM82HnFVVX19KY1uA5mu0QqEWhqKpoW3svG3XHrXDiCQjK1mdgAvhCscA9GE&client_secret=1955279925675241571&
refresh_token=your token here
```