Skip to content

[Android][BLITZ] Native Login WebView fallback does not show back button - #2994

Merged
JohnsonEricAtSalesforce merged 1 commit into
forcedotcom:devfrom
JohnsonEricAtSalesforce:bugfix/W-17912094_android-native-login-webview-fallback-back-button
Aug 15, 2026
Merged

[Android][BLITZ] Native Login WebView fallback does not show back button#2994
JohnsonEricAtSalesforce merged 1 commit into
forcedotcom:devfrom
JohnsonEricAtSalesforce:bugfix/W-17912094_android-native-login-webview-fallback-back-button

Conversation

@JohnsonEricAtSalesforce

@JohnsonEricAtSalesforce JohnsonEricAtSalesforce commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Summary

When an app uses Native Login, the WebView-based LoginActivity acts as a dismissible fallback login
surface. Hardware/gesture back already worked there — LoginActivity.handleBackBehavior() finishes the activity
and returns to the native login activity when nativeLoginActivity != null. But the visible top-bar back
affordance never appeared, so a user who reached the fallback WebView had no on-screen way back and could get
stranded.

The visible affordance is bound to LoginViewModel.shouldShowBackButton, which previously gated only on
whether there were authenticated users. This change makes the visible button match the already-correct back
behavior in the Native Login fallback case.

Root cause

LoginViewModel.shouldShowBackButton returned
!(authenticatedUsers.isNullOrEmpty() || biometricAuthenticationManager?.locked). In Native Login fallback
mode there are typically no authenticated users yet, so the button was suppressed — even though
handleBackBehavior() would have dismissed the screen.

Fix

libs/SalesforceSDK/src/com/salesforce/androidsdk/ui/LoginViewModel.kt — rewrote the property as a when:

open val shouldShowBackButton = with(SalesforceSDKManager.getInstance()) {
    when {
        // Never show back while biometric-locked; user must authenticate.
        biometricAuthenticationManager?.locked == true -> false
        /*
         * Native Login uses this WebView LoginActivity as a dismissible
         * fallback: handleBackBehavior() finishes and returns to the native
         * login activity, so show the back affordance to match — even with
         * no authenticated users yet.
         */
        nativeLoginActivity != null -> true
        // Otherwise show back only when an authenticated user exists.
        else -> !userAccountManager.authenticatedUsers.isNullOrEmpty()
    }
}

Behavior preserved:

  • Biometric-locked still suppresses the button (user must authenticate).
  • Standard login-without-accounts flow (moveTaskToBack) is unchanged.
  • NativeLoginManager.shouldShowBackButton (the app's own native login screen) is a different surface and is
    intentionally not touched — it correctly continues to gate on authenticated users.

Tests

libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/auth/LoginViewModelTest.kt — 2 new tests:

  • test_shouldShowBackButton_isTrueForNativeLoginFallbackWithNoUsers — asserts the button shows when
    nativeLoginActivity != null and there are no authenticated users. Fails on the pre-fix code (failing-test-first).
  • test_shouldShowBackButton_isFalseForNativeLoginFallbackWhenBiometricLocked — asserts biometric-locked still suppresses the button even in fallback mode. Pins the no-authenticated-users precondition so the biometric branch is the only thing that could show the button, proving the lock takes precedence.

Both tests share a small withNativeLoginFallbackViewModel { … } helper that spies SalesforceSDKManager to report Native Login is enabled and always tears the object mock down in a finally.

Verification

1. Instrumented unit tests (API 36 emulator)

  • Branch base == latest dev tip c6fa97b03 (no drift).
  • Failing-test-first: with the production fix reverted, test_shouldShowBackButton_isTrueForNativeLoginFallbackWithNoUsers
    FAILS ("Back button must be shown for the Native Login WebView fallback") — confirms the bug is real and still
    present on latest dev.
  • With the fix: both new tests pass; NativeLoginManagerTest 21/0.
  • Suite flakiness note (pre-existing, not introduced by this PR): repeated full LoginViewModelTest runs
    show occasional order-dependent failures in unrelated tests (generateAuthorizationUrl_*,
    codeVerifier_UpdatesOn_WebViewRefresh). The pristine dev suite flakes at the same ~10% rate and on a
    different unrelated test, so this is pre-existing suite instability, not caused by this change. All such
    tests pass in isolation. Out of scope for this bugfix.

2. End-to-end feature verification against the real Native Login feature (API 36 emulator)

The unit tests above exercise the LoginViewModel in isolation with mocks. To prove the actual user-facing
behavior
, I also ran the AndroidNativeLoginTemplate (from the MSDK Templates repo) against a real
Experience Cloud org with Headless Identity, wired to this branch via a Gradle composite build, and drove the
exact repro on-device: launch → native login screen → tap "Looking for Salesforce Log In?" → WebView
fallback com.salesforce.androidsdk.ui.LoginActivity, with zero authenticated users (the reported broken
scenario). I captured both the screenshot and the uiautomator view hierarchy for each side:

Build WebView LoginActivity toolbar content-desc="Back" node
WITHOUT this fix (tip of dev) only ⋮ overflow — no back button; user is stranded (the bug) absent (0)
WITH this fix (this branch) ← back button present; user can dismiss back to native login present (1) at [43,181][106,244]

The before/after difference is exactly the affordance this PR restores, confirmed both visually and in the
uiautomator accessibility hierarchy. The template clone used real test-org credentials that are not
committed anywhere.

Before — WebView fallback LoginActivity, no authenticated users (tip of dev, without this fix)

Toolbar shows only the ⋮ overflow; there is no back affordance, so the user is stranded.

BEFORE_no-back-button

BEFORE screenshot: no back button in the top-left of the WebView fallback toolbar.

After — same screen, same conditions, with this fix

The ← back affordance is present in the top-left and returns the user to the native login screen.

AFTER_back-button-present

AFTER screenshot: ← back button present in the top-left of the WebView fallback toolbar.

Regression risk

Low, and bounded to Native-Login apps. The rewrite is behavior-preserving for every path except the one
being fixed. Reasoning, per input:

  • The only value that changes is the Native-Login-fallback case. The new when adds exactly one arm keyed on
    nativeLoginActivity != null. That condition is true only when an app has opted into Native Login via
    useNativeLogin(...); apps that don't are completely unaffected.
  • Biometric-locked is evaluated first and still returns false — a locked user can never be shown the
    affordance. Equivalent to the old expression's biometricAuthenticationManager?.locked term, and covered by
    test_shouldShowBackButton_isFalseForNativeLoginFallbackWhenBiometricLocked.
  • Standard login (no Native Login) is unchanged. The else arm is the original predicate
    !userAccountManager.authenticatedUsers.isNullOrEmpty(), so the standard host-picker / login-without-accounts
    flow (which relies on moveTaskToBack) behaves exactly as before.
  • No change to back handling, only to back visibility. LoginActivity.handleBackBehavior() already
    finished the activity and returned to the native login activity in this case; hardware/gesture back already
    worked. This PR only makes the visible affordance agree with behavior that already shipped — it does not add
    a new navigation path.
  • NativeLoginManager.shouldShowBackButton (the app's own native login screen) is a different surface and is
    not touched
    — it continues to gate on authenticated users.
  • Adjacent recent change is safe. The recent server-picker-non-dismissable change
    (Make login server picker non-dismissable (W-23731759) #2983) added a pre-existing guard test,
    test_shouldShowBackButton_isFlagIndependent, asserting the property does not depend on the deprecated
    forceAdvancedAuthentication flag. This change keeps that test green — the flag never enters the new when.
  • No public API signature change. shouldShowBackButton remains an open val of the same type; only its
    computed value changes in the fallback case. No semver/deprecation impact.

Checklist

  • Backward compatibility: no public API signature change (see Regression risk).
  • Tests included: 2 new tests in the correct target; failing-test-first proven; pre-existing
    isFlagIndependent guard still green.
  • No regressions: biometric-locked and standard flows unchanged and covered by tests.
  • Multi-user: unaffected — the new branch is keyed on nativeLoginActivity, orthogonal to account count.
  • Localization: no new user-facing strings.
  • Security: no credential/token handling change; no logging added.
  • Both platforms: Android-only — no matching iOS change is needed for this bug.

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

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown
3 Warnings
⚠️ libs/SalesforceSDK/src/com/salesforce/androidsdk/ui/LoginViewModel.kt#L328 - This method should only be accessed from tests or within private scope
⚠️ libs/SalesforceSDK/src/com/salesforce/androidsdk/ui/LoginViewModel.kt#L332 - This method should only be accessed from tests or within private scope
⚠️ libs/SalesforceSDK/src/com/salesforce/androidsdk/ui/LoginViewModel.kt#L336 - This method should only be accessed from tests or within private scope

Generated by 🚫 Danger

@JohnsonEricAtSalesforce JohnsonEricAtSalesforce changed the title @W-17912094: [Android] Show back button on Native Login WebView fallback [Android][BLITZ] Native Login WebView fallback does not show back button Aug 14, 2026
@JohnsonEricAtSalesforce
JohnsonEricAtSalesforce force-pushed the bugfix/W-17912094_android-native-login-webview-fallback-back-button branch 2 times, most recently from 3d41cfe to 6c45d2c Compare August 14, 2026 22:54
@JohnsonEricAtSalesforce
JohnsonEricAtSalesforce marked this pull request as ready for review August 14, 2026 22:57
* login activity, so show the back affordance to match — even with
* no authenticated users yet.
*/
nativeLoginActivity != null -> true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This doesn't seem right. The screen should not show a back button unconditionally for native login. What if there are 0 users logged in?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good question — I checked the 0-users case specifically, and it's not a dead-end.

The reason it's safe to show unconditionally for native login is that the back handler is already unconditional in that mode. LoginActivity.handleBackBehavior() finishes and returns to the native login activity whenever nativeLoginActivity != null — it never consults the user count:

// LoginActivity.handleBackBehavior()
if (nativeLoginActivity != null) {
    setResult(RESULT_CANCELED)
    finish()
    return
}

So with native login enabled, hardware/gesture back already dismisses this WebView fallback back to the native screen — including with 0 users. Before this change the visible ← button was gated on authenticated users, so it was hidden even though the navigation behind it worked, leaving no visible affordance to escape the fallback. This change just makes the button match the behavior that already ships.

I verified the 0-users path end-to-end on-device (native login enabled, no accounts): the fallback shows ←, and both the button and the gesture return to the native login screen — no dead-end. Happy to attach screenshots if useful.

Note this is scoped to LoginViewModel.shouldShowBackButton (the WebView fallback); NativeLoginManager.shouldShowBackButton — the app's own native screen — is untouched and still gates on users.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I forgot we have a separate shouldShowBack for native login.

@brandonpage brandonpage left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This does not fix the issue of showing the back button when appropriate, it just always shows it for native login.

…rue from LoginViewModel.shouldShowBackButton when nativeLoginActivity is set so the dismissible WebView fallback shows the affordance; add tests)
@JohnsonEricAtSalesforce
JohnsonEricAtSalesforce force-pushed the bugfix/W-17912094_android-native-login-webview-fallback-back-button branch from 6c45d2c to 4b45a1e Compare August 14, 2026 23:43
@JohnsonEricAtSalesforce
JohnsonEricAtSalesforce merged commit e773b29 into forcedotcom:dev Aug 15, 2026
5 of 6 checks passed
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.

3 participants