Default DPoP on for new logins in MSDK 14 - #2996
Conversation
wmathurin
left a comment
There was a problem hiding this comment.
Are there updates needed in native/NativeSampleApps/AuthFlowTester/README.md ?
I noticed a divergence between iOS and Android for LegacyLoginTests (I guess some of the iOS legacy tests are in the CAScopeSelectionLoginTests). Should we move tests around to make them more similar ??
JohnsonEricAtSalesforce
left a comment
There was a problem hiding this comment.
The flag flip itself is correct and well-scoped: I traced every read of useDPoP and it only feeds new-login paths (the login-options UI seed and the /authorize dpop_jkt gate in addDpopJktIfNeeded). Resource-time attachment goes through the per-credential DPoPKeyManager.shouldAttachDPoP(tokenType) gate that landed in PR 2981, which fast-exits Bearer credentials — so flipping the default cannot retroactively bind existing Bearer sessions. The KDoc update saying so is accurate and worth having. Two test issues need fixing before this merges.
1. Test not updated for the new default (blocking).
LoginViewModelTest.selectedServer_Changes_GenerateCorrectAuthorizationUrl (in the SalesforceSDKTest instrumented target, run under the unit-tests-pr check) fails. I reproduced it locally on an api36 emulator:
expected: …YQV9KHv_teaXsor5cP6Q
actual: …YQV9KHv_teaXsor5cP6Q&dpop_jkt=WdHHE-FZZCXootlaTGazbEjPNJkU6KbwRd1Kg-2cxhY
The test's helper generateExpectedAuthorizationUrl builds the expected /authorize URL with an empty extra-params map, so it never contains dpop_jkt. The test's first assertion passes because the default server is login.salesforce.com (a pool server, which addDpopJktIfNeeded excludes); the second assertion fails once selectedServer switches to shouldMatchNothing.salesforce.com — a my-domain (non-pool) server — because with the default now true, addDpopJktIfNeeded appends &dpop_jkt=… to the actual loginUrl. This test file isn't touched by the PR. Please update the expected-URL assertion/helper to account for dpop_jkt on my-domain servers under the new default.
2. The "free DPoP coverage" for CA-opaque default-path tests isn't actually wired (blocking).
BootConfigLoginTests.testCAOpaque_DefaultScopes_WebServerFlow fails with "Expected no 'DP' flag for non-DPoP session." The reason: a default-path CA_OPAQUE login has needsLoginOptions == false, so the login-options screen is skipped and disableDPoP() never runs — the login binds DPoP under the new global default and the UA gets the DP marker — but loginAndValidate(useDPoP: Boolean = false) still defaults false, so validateUser(isDpop = false) asserts no DP marker. The cleanup() reset was flipped to useDPoP = true but the loginAndValidate param default was not, so they now contradict each other.
Worth looking at how the iOS twin (4134) structured this, since iOS doesn't hit this gap: iOS keys the DP-marker assertion off the app config (userAppConfig.isDPoP) rather than the login param, and its login() unconditionally applies the DPoP toggle, so passing useDPoP: false truly disables it. Two ways to fix on Android: (a) flip the loginAndValidate / helper useDPoP defaults to true to match cleanup() (LegacyLoginTests already passes false explicitly, so it stays a Bearer holdout); or (b) have the CA_OPAQUE default-path callers pass useDPoP = true. Option (a) mirrors iOS most closely.
Notes (non-blocking)
- The remaining ui-tests-pr failures (roughly two dozen distinct tests across unrelated classes) all carry the "Failed to inject touch input" Custom-Tab hand-off signature — the known environment flake, not this PR. Flagging so it's not mistaken for fallout from the flip. It does mean finding 2 is under-reported by the harness, but the one clean CA_OPAQUE default-path failure already surfaces it.
LegacyLoginTestsas a dedicated Bearer holdout (forcing DPoP off via@Before+ explicit per-calluseDPoP = false) is a good pattern and matches the iOS twin.
This review was generated by an AI agent on behalf of @JohnsonEricAtSalesforce.
8b01567 to
4977aa8
Compare
|
@JohnsonEricAtSalesforce This #2995 PR is the bigger and more important one, and will affect this PR. I will address your feedback that you made here in this PR once 2995 goes in first. |
|
Fixed in 21c9c17 — thanks for catching both. Updated
Verified the toggle wiring: |
Flip SalesforceSDKManager.useDPoP default from false to true. DPoP (RFC 9449) is now validated by the server, so new apps and new logins request DPoP-bound tokens by default. The per-credential attachment gate keeps existing Bearer credentials Bearer and existing DPoP credentials bound, so the change governs new logins only. Tests: - AuthFlowTest.cleanup() resets useDPoP to the new true default between tests, so non-overriding UI classes gain DPoP coverage for free. - Add LegacyLoginTests as the dedicated Bearer (non-DPoP) class using the CA opaque config, forcing DPoP off via a @before and per-call override. - Update DPoPLoginTests class doc for the new reset value and add its missing SalesforceSDKManager import so androidTest compiles.
MSDK 14 defaults DPoP on for new logins, and this PR changes the test cleanup() reset from false to true accordingly. Update the README so it no longer says cleanup() resets DPoP to false, and fix the manual-testing Login Options entry: the Use DPoP toggle now defaults on (initialized from SalesforceSDKManager.useDPoP). Turning it off is the explicit Bearer compatibility path.
21c9c17 to
2def8a4
Compare
The SDK 14.0 useDPoP default of true appends a dpop_jkt parameter to the generated authorization URL. The general URL-shape assertions in this class build their expected URLs without dpop_jkt, so pin the flag off for the default view model under test and restore it in teardown. The dedicated dpop_jkt tests use a mock manager with an explicit useDPoP value and are unaffected.
The UI tests build the OAuth authorization URL from the global useDPoP flag, read at URL-generation time. On the CA opaque all-defaults path the login helper skips the Login Options screen, so the DPoP toggle is never applied there; under the new useDPoP=true default that left those logins DPoP-bound while their assertions expected Bearer. Pin useDPoP off in a base @before (and reset to off, not true, in cleanup) so every test starts Bearer. DPoP tests re-enable it explicitly through the Login Options toggle, whose non-CA-opaque configs always open that screen. This mirrors iOS, where the DPoP toggle is always applied on the login-options surface rather than inferred from a helper parameter, and keeps the fast skip path on its existing surface. LegacyLoginTests no longer needs its own forceBearer hook now that the base class provides the baseline.
|
@JohnsonEricAtSalesforce Both findings addressed now that #2995 is in. Two focused test-only commits on top of the flip:
Finding 1 — Finding 2 — CA-opaque default-path I did try your option (a) (flip the helper defaults to So I mirrored what iOS actually does at the mechanism level instead: iOS keeps its helper defaults Net effect vs. the "free DPoP coverage" framing: DPoP coverage stays where it's explicit ( Verification note. Finding 1 is green. For finding 2, the fix compiles clean and the one CA-opaque run that got past the Custom Tab passed |
What
Flips
SalesforceSDKManager.useDPoPfromfalsetotrue. DPoP (RFC 9449) is now validated by the server, so new apps and new logins request DPoP-bound tokens by default.The change governs new logins only — the per-credential attachment gate keeps existing Bearer credentials Bearer and existing DPoP credentials bound.
Changes
SalesforceSDKManager.kt— default flipped totrue.AuthFlowTest.cleanup()— resetsuseDPoPto the newtruedefault between tests, so non-overriding UI classes gain DPoP coverage for free.LegacyLoginTests— added as the dedicated Bearer (non-DPoP) class using the CA opaque config, forcing DPoP off via a@Beforeand per-call override.DPoPLoginTests— class doc updated for the new reset value; added the missingSalesforceSDKManagerimport soandroidTestcompiles.Rebase note
This is a small, self-contained flip that overlaps the DPoP upgrade/migration work in #2995 (both touch DPoP defaults and the AuthFlowTester test classes). Whichever of the two merges first, the other will need a rebase. No preference on order.
Testing
Tested and passing.