Repository navigation
W-24224593: Select request authentication by path - #3041
Conversation
JohnsonEricAtSalesforce
left a comment
There was a problem hiding this comment.
Traced the header-selection path end to end (initial build, nonce retry, refresh replay) and couldn't find a case where a stale scheme choice survives a replay: the decision is recomputed fresh on every call, and the new synchronized on buildAuthenticatedRequest closes a race against the already-synchronized credential writers. Compatibility holds throughout via additive overloads and a new default interface method.
Compared against the merged iOS twin (PR 4175): the policy shape and nonce-retry guard match across platforms. Each platform's tracking item describes the replay coverage slightly differently ("refresh and replay" vs. "retries"), and that turns out to reflect a real architecture difference rather than a coverage gap — Android's per-user interceptor cache needs the new adoptCredentialMetadataIfSameAuthToken guard to avoid a stale snapshot rolling back a newer credential's metadata; iOS has no equivalent cache to reconcile.
One item below is worth addressing before merge; two more are small.
Unguarded policy callback risks a crash and lock contention
libs/SalesforceSDK/src/com/salesforce/androidsdk/auth/dpop/DPoPRequestDecorator.kt:86 invokes the app-supplied shouldUseUiSidBearerForPath policy with no try/catch. That call now happens inside RestClient.buildAuthenticatedRequest, which this PR changes to synchronized on the per-user cached interceptor's monitor. Two consequences follow from that combination: a policy that throws propagates uncaught out of buildAuthenticatedRequest — inconsistent with attachProof a few lines below in the same file, which already wraps its body in try/catch and logs failures; and a policy that blocks (touches disk, waits on another lock, etc.) stalls every other thread's request against that same cached interceptor while holding the lock, including the nonce-retry and refresh-replay paths that also need it. Since the policy is app-supplied and now runs under a lock shared across requests for a user, suggest wrapping the invocation in try/catch with a fallback to DPoP on failure (matching the existing "never silently drop auth" handling for a missing uiSid), and documenting in KDoc that the callback must be fast and non-blocking.
Silent no-op on credential mismatch (small)
libs/SalesforceSDK/src/com/salesforce/androidsdk/rest/RestClient.java:246, adoptCredentialMetadataIfSameAuthToken — silently no-ops (dropping a fresh, possibly-correct uiSid) whenever the candidate authToken doesn't exactly match the cached one, with no logging. This is the intended staleness guard, not a bug, but if it ever fires unexpectedly there's currently no way to tell from the logs. Suggest a debug-level log line on the mismatch branch.
Stale comment reference to a deleted method (small)
libs/test/SalesforceSDKTest/src/com/salesforce/androidsdk/rest/RestClientDPoPGateTests.kt, around the comment on test_givenNoTokenTypeButExistingKeyPair_whenIntercept_thenAttachesProof:
// Belt-and-suspenders: no tokenType, but a key pair exists for the credential
// → interceptor still attaches a DPoP proof (Authorization stays Bearer
// because setAuthHeader keys off exact tokenType match — this asymmetry
// is intentional and documented).
setAuthHeader was the private method this PR removes from RestClient.OAuthRefreshInterceptor (its behavior now lives in DPoPRequestDecorator.applyAuthHeaders). The comment predates this PR, but this PR's own deletion is what makes it definitively stale — the named method no longer exists anywhere in the codebase. The underlying claim (exact-tokenType-match asymmetry) still holds; only the method name needs updating.
Verdict: Request changes. The unguarded policy callback is the one item blocking approval — everything else here is optional.
This review was generated by an AI agent on behalf of @JohnsonEricAtSalesforce.
…arer-path # Conflicts: # docs/auth/token-lifecycle.md
|
Addressed the review in f4db199 (after merging current upstream/dev in e673e50): the app-supplied path policy is now exception-contained with a fail-safe DPoP fallback, its KDoc and auth documentation require fast/non-blocking behavior, credential-generation mismatch emits a generic debug log without credential values, and the stale test comment now names DPoPRequestDecorator.applyAuthHeaders. Added a regression test proving a throwing policy still produces DPoP authorization and proof headers. Validation: full :libs:SalesforceSDK:build plus assembleAndroidTest passed, and 100 focused emulator tests passed with 0 failures. |
JohnsonEricAtSalesforce
left a comment
There was a problem hiding this comment.
Verified the fix commit (f4db19948) against the three items from the prior review round:
- Unguarded policy callback —
DPoPRequestDecorator.ktnow callsshouldUseUiSidBearerForPaththrough a private wrapper that catchesException, logs a warning, and falls back tofalse(normal DPoP/Bearer path). KDoc onSalesforceSDKManager.shouldUseUiSidBearerForPathanddocs/auth/token-lifecycle.mdnow state the callback must be fast and non-blocking, and document the fail-safe. A new test (applyAuthHeaders_dpopWithUiSidWhenPolicyThrows_fallsBackToDpopHeaders) confirms a throwing policy still producesDPoPauthorization and proof headers. - Silent no-op on credential mismatch —
RestClient.java'sadoptCredentialMetadataIfSameAuthTokennow logs at debug level on the mismatch branch, with no credential values in the message. - Stale comment — the test comment in
RestClientDPoPGateTests.ktnow correctly namesDPoPRequestDecorator.applyAuthHeadersinstead of the removedsetAuthHeader.
All three read as complete and correct; no new issues introduced by the fix itself.
Verdict: Approve.
This review was generated by an AI agent on behalf of @JohnsonEricAtSalesforce.
Summary
SalesforceSDKManagersynchronously chooseui_sidBearer authentication by request path when a DPoP access token is present.ui_sidBearer, and all other requests keep DPoP.Test plan
assembleDebug: passed.