Loading
Prepare for Email to Become the Default Login ExperienceRead More
Identify Your Users and Manage Access
OAuth 2.0 DPoP Guide

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

Available in: all editions

DPoP is defined in RFC 9449: OAuth 2.0 Demonstrating Proof of Possession (DPoP). 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

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 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) 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).

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

OAuth 2.0 DPoP Supported Flows and Endpoints

The OAuth 2.0 Demonstrating Proof-of-Possession (DPoP) extension protects access to your Salesforce data. It works by cryptographically binding OAuth access and refresh tokens to the client that requested them. Even if an attacker gets access to a token, they can’t use it to access your data. You can use DPoP with most OAuth flows, and you can use DPoP-bound access tokens to request access to Salesforce resources for supported endpoints.

Required Editions

Available in: all editions

OAuth Flows

These OAuth 2.0 flows support DPoP:

  • Client credentials flow
  • Device flow
  • Headless login, registration, passwordless login, and guest flows
  • Hybrid web-server and refresh token flows
  • JWT bearer flow
  • Refresh token flow
  • Token exchange flow
  • SAML bearer assertion flow

All other flows, including the username-password, user-agent, and hybrid-user agent flows, aren’t supported.

Endpoints

Salesforce supports DPoP for My Domain and Experience Cloud sites. You can use DPoP-bound access tokens to access these endpoints:

 
Loading
Salesforce Help | Article