OAuth 2.0 Demonstrating Proof of Possession (DPoP)
Starting in Mobile SDK 14.0, apps obtain DPoP-bound OAuth tokens by default. DPoP (Demonstrating Proof of Possession) is an OAuth 2.0 security mechanism that binds access and refresh tokens to a key pair held on the device. DPoP prevents stolen tokens from being replayed by a different client. If an attacker intercepts a token, it’s useless without the corresponding private key, which never leaves the device.
When DPoP is turned on in the client—the default in Mobile SDK 14.0—the SDK presents a DPoP proof at login and on every API call. Salesforce validates the proof and issues DPoP-bound tokens. Until an external client app or connected app is configured to require DPoP, the server also accepts standard unbound (Bearer) tokens. Adopt DPoP gradually without disrupting clients that aren’t yet DPoP-bound.
Your app’s boot config doesn’t require changes to support DPoP. The client flag and the server configuration drive DPoP behavior. This page covers how the server treats DPoP, how to set the client flag, how hybrid remote apps get a UI session ID, and where DPoP keys are stored.
A Salesforce admin configures DPoP per external client app or connected app, in that app’s OAuth Settings. The server supports two postures.
Posture
Server Behavior
DPoP allowed (default)
The server validates DPoP proofs when a client presents them and issues DPoP-bound tokens. It still accepts unbound (Bearer) tokens. Clients that don’t send proofs keep working. Use this posture while you roll DPoP out.
DPoP required
The server accepts only DPoP-bound tokens and rejects unbound (Bearer) requests.
Don’t require DPoP until every client is DPoP-capable and its users hold bound tokens. Requiring DPoP logs out any user who is still on a Bearer session. See Migrate Existing Sessions to DPoP.
Warning
Controlling DPoP on the Client
By default, Mobile SDK 14.0 turns DPoP on. Every new login presents a DPoP proof and receives a DPoP-bound token from Salesforce. Most apps don’t need to change anything. To control this behavior explicitly, set the flag before login.
Android
1SalesforceSDKManager.getInstance().useDPoP = true // default is true
iOS
1SalesforceManager.shared.usesDPoP = true // default is true
The flag governs new logins only. It controls whether the SDK requests a DPoP-bound token at the next login. It doesn’t affect sessions that are already established.
After a session is DPoP-bound, Mobile SDK keeps attaching DPoP proofs to that session’s requests, regardless of the flag’s later value. Turning the flag off doesn’t downgrade an existing DPoP session. It only changes what future logins request.
Because Mobile SDK 14.0 turns DPoP on by default, gradual rollout means explicitly setting the flag to false at startup, then enabling it for a growing share of installs. If you need to pause, set the flag back to false. The server accepts both DPoP-bound and Bearer tokens while DPoP is allowed. Users who logged in while the flag was off stay on Bearer tokens until they log in again, or until you migrate them with upgradeToDPoP().
Note
Send Requests with DPoP
Mobile SDK attaches the correct Authorization and DPoP headers automatically for any request that you send through RestClient on Android or RestClient (SFRestAPI) on iOS.
If your app builds and sends its own HTTP requests directly, use DPoPRequestDecorator.applyAuthHeaders(...) to stamp the right headers based on the account’s current credential state. The decorator sets Authorization: Bearer <token> for a standard session and Authorization: DPoP <token> plus a fresh DPoP proof header for a DPoP-bound session. The same call is correct whether or not the session uses DPoP.
Android (OkHttp)
1val builder = Request.Builder().url(url)2DPoPRequestDecorator.applyAuthHeaders(builder, userAccount)3val request = builder.build()4// send `request` on your own OkHttpClient
iOS
1let mutableRequest = NSMutableURLRequest(url: url)2try DPoPRequestDecorator.applyAuthHeaders(mutableRequest, credentials: credentials)3// send `mutableRequest` on your own URLSession
DPoP and Hybrid Remote Apps
Hybrid remote apps load Salesforce content, such as Visualforce or Lightning pages, in a WebView. Those apps inject a Salesforce session token as a cookie. A DPoP-bound access token can’t be used this way, because WebView requests don’t carry DPoP proofs. For DPoP sessions, the server returns a separate, unbound UI session ID (ui_sid) that’s safe for WebView cookie injection.
To get the correct session token regardless of auth type, use the SDK accessor instead of reading the access token directly.
Android: UserAccount.getMainSid() returns ui_sid for DPoP sessions, parentSid for JWT-format Bearer sessions, or the access token otherwise.
iOS: SFOAuthCredentials.mainSid uses the same resolution.
Mobile SDK’s hybrid container already uses this accessor to inject the WebView session cookie, so standard hybrid remote apps need no changes. Use the accessor directly only if your app performs its own WebView cookie injection.
Key Storage
DPoP key pairs are generated once per user account and stored using the platform’s secure key store.
iOS: Stored in the iOS Keychain (Secure Enclave where available). Keys are bound to the user’s credential set and aren’t exportable.
Android: Stored in the Android Keystore. Keys are hardware-backed on supported devices and aren’t extractable.
Mobile SDK manages the key lifecycle automatically. Keys are removed when the associated user account is logged out or removed from the device.