Skip to content

[Android] fetchIsSalesforceIntegrationUser omits DPoP proof on /userinfo, causing Bad_OAuth_Token login failure with DPoP-bound tokens - #3044

Open
JohnsonEricAtSalesforce wants to merge 5 commits into
forcedotcom:devfrom
JohnsonEricAtSalesforce:bugfix/android-fetchissalesforceintegrationuser-omits-dpop-proof-on-userinfo-causing-bad-oauth-token-login-failure-with-dpop-bound-tokens
Open

JohnsonEricAtSalesforce wants to merge 5 commits into
forcedotcom:devfrom
JohnsonEricAtSalesforce:bugfix/android-fetchissalesforceintegrationuser-omits-dpop-proof-on-userinfo-causing-bad-oauth-token-login-failure-with-dpop-bound-tokens

Conversation

@JohnsonEricAtSalesforce

Copy link
Copy Markdown
Contributor

Summary

fetchIsSalesforceIntegrationUser() called /services/oauth2/userinfo using the 2-arg
OAuth2.addAuthorizationHeader() overload, which always attaches a plain Bearer header and
never a DPoP proof. For orgs using DPoP-bound tokens, this made the integration-user check fail
with Bad_OAuth_Token, breaking login for any app with shouldBlockSalesforceIntegrationUser = true.

This brings the check to parity with the DPoP-aware sibling, OAuth2.callIdentityService():

  • Attaches a DPoP proof via DPoPRequestDecorator.attachProof when the token is DPoP-bound
    (shared with the initial request and the redirect-reattachment path via a new
    attachAuthHeaders helper).
  • Handles a DPoP nonce-challenge retry (one retry, mirroring callIdentityService).
  • Extracted the redirect network-interceptor logic into a standalone, independently testable
    reattachAuthOnRedirect function.
  • Preserves fail-closed behavior on non-2xx responses or malformed JSON (throws IOException,
    same as before this change) — but that throw was only being caught at 2 of 4 call sites. The
    other two (LoginActivity's User-Agent-flow branch and NativeLoginManager.suspendFinishAuthFlow)
    had no exception handling, so a transient network/parse error during this check would have
    crashed the login coroutine instead of surfacing a graceful error. Added matching try/catch
    guards at both sites (LoginActivity.kt, NativeLoginManager.kt), consistent with the existing
    guarded call sites (LoginViewModel.doCodeExchange, SPAuthCodeHelper.loginWithAuthCode).

iOS

iOS already goes through SFSDKDPoPRequestDecorator applyAuthHeaders: for the equivalent check
in SFUserAccountManager.m/SFIdentityCoordinator.m — no iOS gap, no companion PR needed.

Test plan

  • AuthenticationUtilitiesIntegrationUserTest (new, 7 tests) — DPoP-bound attaches proof,
    Bearer never attaches proof, nonce-challenge retries once, non-JSON error body →
    IOException not JSONException, malformed 200 body → IOException, redirect to a
    Salesforce host reattaches Authorization+DPoP, no redirect attaches nothing.
  • NativeLoginManagerTest#nativeLoginManager_login_convertsOnAuthFlowCompleteExceptionToUnknownError
    (new) — confirms the new catch guard converts a thrown exception to NativeLoginResult.UnknownError.
  • LoginActivityTest#authWebViewClient_shouldOverrideUrlLoading_convertsOnAuthFlowCompleteExceptionToOnAuthFlowError
    (new) — confirms the new catch guard routes to onAuthFlowError instead of crashing.
  • Regression, all green: AuthenticationUtilitiesTest 42/42, OAuth2DPoPTest 10/10,
    NativeLoginManagerTest 24/24, LoginActivityTest 56/56, LoginActivityScenarioTest 14/14.
  • End-to-end verification against a live DPoP-capable org — not run locally (no
    test_credentials.json/ui_test_config.json available in this environment). Relying on the
    unit-test coverage above; deferring to CI/reviewer for E2E confirmation.

This response was generated by an AI agent on behalf of @JohnsonEricAtSalesforce.

…itted DPoP proof on /userinfo causing Bad_OAuth_Token login failure)
nativeLogin = true,
)
try {
onAuthFlowComplete(

@github-actions github-actions Bot Sep 19, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ This method should only be accessed from tests or within private scope

@github-actions

github-actions Bot commented Sep 19, 2026

Copy link
Copy Markdown
1 Warning
⚠️ libs/SalesforceSDK/src/com/salesforce/androidsdk/ui/LoginActivity.kt#L1263 - Consider adding a <queries> declaration to your manifest when calling this method; see https://g.co/dev/packagevisibility for details

Generated by 🚫 Danger

… prefer-static-imports convention for new test code)
…new test (Follow prefer-static-imports convention, missed on first pass)
…ch the direct-import convention used at 7 of 9 repo call sites)
…field (Complete prefer-static-imports pass, drop dead cursor field)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant