-
Notifications
You must be signed in to change notification settings - Fork 393
@W-24269726 fix: Reject incomplete Android DPoP credentials #3048
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
87e4287
c3e7890
c26d861
ea75f47
fb655be
3384468
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,6 +32,7 @@ import com.salesforce.androidsdk.auth.OAuth2 | |
| import com.salesforce.androidsdk.util.SalesforceSDKLogger | ||
| import okhttp3.Request | ||
| import okhttp3.Response | ||
| import java.io.IOException | ||
|
|
||
| /** | ||
| * Public convenience API for app developers stamping DPoP/Bearer authorization | ||
|
|
@@ -51,17 +52,22 @@ object DPoPRequestDecorator { | |
| private const val NONCE_ERROR_VALUE = "use_dpop_nonce" | ||
|
|
||
| /** | ||
| * Stamps the complete [Authorization] and optional [DPoP] proof header set on [builder]. | ||
| * No-op when [UserAccount.getAuthToken] is null or empty. | ||
| * Stamps [Authorization] and, if the account is DPoP-bound, a [DPoP] proof header | ||
| * on [builder]. No-op when [UserAccount.getAuthToken] is null or empty. Rejects an | ||
| * incomplete DPoP credential with [IOException] before mutating [builder]. | ||
| */ | ||
| fun applyAuthHeaders(builder: Request.Builder, userAccount: UserAccount) { | ||
| val tokenType = userAccount.tokenType | ||
| val credentialsIdentifier = userAccount.credentialsIdentifier | ||
| requireCompleteDPoPCredentials(credentialsIdentifier, tokenType) | ||
|
|
||
| val authToken = userAccount.authToken ?: return | ||
| if (authToken.isEmpty()) return | ||
|
|
||
| applyAuthHeaders( | ||
| builder, | ||
| userAccount.credentialsIdentifier, | ||
| userAccount.tokenType, | ||
| credentialsIdentifier, | ||
| tokenType, | ||
| authToken, | ||
| userAccount.uiSid, | ||
| ) | ||
|
|
@@ -80,6 +86,7 @@ object DPoPRequestDecorator { | |
| authToken: String?, | ||
| uiSid: String?, | ||
| ) { | ||
| requireCompleteDPoPCredentials(credentialsIdentifier, tokenType) | ||
| if (authToken.isNullOrEmpty()) return | ||
|
|
||
| val requestPath = builder.build().url.encodedPath | ||
|
|
@@ -139,7 +146,8 @@ object DPoPRequestDecorator { | |
|
|
||
| /** | ||
| * Package-private helper used by RestClient.OAuthRefreshInterceptor to delegate | ||
| * the proof-building step without constructing a UserAccount. | ||
| * the proof-building step without constructing a UserAccount. An incomplete DPoP | ||
| * credential throws [IOException] so OkHttp reports it through normal request failure. | ||
| */ | ||
| @JvmName("attachProof") | ||
| internal fun attachProof( | ||
|
|
@@ -148,6 +156,7 @@ object DPoPRequestDecorator { | |
| tokenType: String?, | ||
| authToken: String? | ||
| ) { | ||
| requireCompleteDPoPCredentials(credentialsIdentifier, tokenType) | ||
|
wmathurin marked this conversation as resolved.
|
||
| if (!DPoPKeyManager.shouldAttachDPoP(credentialsIdentifier, tokenType)) return | ||
| try { | ||
| val request = builder.build() | ||
|
|
@@ -164,4 +173,17 @@ object DPoPRequestDecorator { | |
| SalesforceSDKLogger.e(TAG, "Failed to attach DPoP proof", e) | ||
| } | ||
| } | ||
|
|
||
| private fun requireCompleteDPoPCredentials( | ||
| credentialsIdentifier: String?, | ||
| tokenType: String? | ||
| ) { | ||
| if (!DPoPKeyManager.hasCompleteDPoPCredentials(credentialsIdentifier, tokenType)) { | ||
| throw IncompleteDPoPCredentialsException() | ||
| } | ||
| } | ||
|
|
||
| private class IncompleteDPoPCredentialsException : IOException( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One small observation: IncompleteDPoPCredentialsException is currently defined as private inside DPoPRequestDecorator.kt. Because it's private, external consumer applications or other internal SDK orchestrators/synchronization engines (such as SyncTask) won't be able to catch this specific exception class. They will only see a generic IOException, meaning they cannot easily differentiate between a local corrupt/incomplete credential state and a standard network issue (like a transient socket timeout) to trigger proactive recovery/telemetry. If there is any plan or possibility that callers would want to programmatically detect and recover from this state, it might be worth exposing it as internal or package-private (similar to RefreshTokenRevokedException or MalformedTokenException). |
||
| "DPoP credentials require a non-blank credentials identifier" | ||
| ) | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.