Skip to content

[iOS] NativeLoginManager Basic-Auth header uses URL-safe Base64 instead of standard Base64, breaking login for some username/password combinations - #4160

Closed
JohnsonEricAtSalesforce wants to merge 1 commit into
forcedotcom:devfrom
JohnsonEricAtSalesforce:fix/W-24044192-basic-auth-base64
Closed

[iOS] NativeLoginManager Basic-Auth header uses URL-safe Base64 instead of standard Base64, breaking login for some username/password combinations#4160
JohnsonEricAtSalesforce wants to merge 1 commit into
forcedotcom:devfrom
JohnsonEricAtSalesforce:fix/W-24044192-basic-auth-base64

Conversation

@JohnsonEricAtSalesforce

Copy link
Copy Markdown
Contributor

Summary

NativeLoginManagerInternal's helper for building the Basic-Auth Authorization header (generateColonConcatenatedBase64String()) encoded the colon-concatenated username and password with the URL-safe Base64 alphabet and no padding (via urlSafeBase64Encode()). HTTP Basic Authentication (RFC 7617) requires the standard Base64 alphabet with padding. For most credential pairs the two alphabets produce identical output, which is why this went unnoticed — they only diverge when the encoded bytes would contain +, /, or need = padding under the standard alphabet. When that happens, login fails with a generic "check your username and password" error even though the credentials are correct (confirmed working via web login for the same user).

Same defect class as the already-fixed, closed Android bug W-23986240 — the iOS Swift implementation independently had the identical bug in its own encoding helper, and the Android fix was never ported to iOS.

  • Changed generateColonConcatenatedBase64String() to encode with the standard Base64 alphabet (plain data.base64EncodedString()) instead of urlSafeBase64Encode().
  • This helper is shared by login(), submitAuthorizationRequest() (used by the OTP/registration-completion flows), and submitPasswordlessAuthorizationRequest() (OTP/passwordless login), so all three call sites are fixed by the one change.

Scope note

This does not touch urlSafeBase64Encode()'s other callers in the same file — generateCodeVerifier() and generateChallenge() — which correctly need URL-safe Base64 per RFC 7636 for PKCE codeVerifier/codeChallenge values and are unrelated to this HTTP Basic-Auth header.

Test plan

  • Added a regression test covering login(), using a username/password pair whose combined UTF-8 bytes diverge between the standard and URL-safe alphabets (contains / and requires = padding under the standard alphabet); asserts the Authorization header matches standard Base64 output.
  • Added a regression test covering submitPasswordlessAuthorizationRequest() (a call site sharing the same helper), same divergent credential pair.
  • Ran NativeLoginManagerTests on the iOS simulator — both new tests pass. (Two pre-existing, unrelated failures in this suite — testShouldShowBackButton/testShouldShowBackButtonWithBioAuth — are keychain-entitlement issues in this local sandboxed environment, reproduced identically on unmodified dev.)
  • Found via a real reproduction during release validation: fresh self-registration on iOS 26 (Native Login template) followed by an immediate logout and login attempt with the identical, just-set password failed; the same password worked correctly via Salesforce web login for the same user, isolating the defect to this SDK's Basic-Auth header encoding.

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

generateColonConcatenatedBase64String() encoded the username:password
credentials with the URL-safe alphabet and no padding, but RFC 7617 HTTP
Basic Authentication requires the standard alphabet with padding. For most
credentials the two alphabets produce identical output, but they diverge
whenever the encoded bytes would contain +, /, or trailing = padding under
the standard alphabet, silently breaking login for the affected accounts.

Same defect class as the closed Android bug W-23986240 (fixed by dropping
the URL_SAFE/NO_PADDING flags); the iOS Swift implementation independently
had the identical bug and the fix was never ported over.

Add regression tests covering both call sites that share this helper
(login and the OTP/passwordless authorization flow) using a credential
pair chosen to diverge between the two alphabets.
@github-actions

Copy link
Copy Markdown
TestsPassedSkippedFailed ❌️
AuthFlowTester UI Test Results all1 ran1 ❌
TestResult
AuthFlowTester UI Test Results all
AuthFlowTesterUITests.xctest
LegacyLoginTests.testCAOpaque_DefaultScopes_WebServerFlow()❌ failure

@codecov

codecov Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 67.57%. Comparing base (1f2b5d8) to head (f0ae047).
⚠️ Report is 3 commits behind head on dev.

Additional details and impacted files
@@            Coverage Diff             @@
##              dev    #4160      +/-   ##
==========================================
- Coverage   71.47%   67.57%   -3.91%     
==========================================
  Files         254      254              
  Lines       22868    22900      +32     
==========================================
- Hits        16346    15474     -872     
- Misses       6522     7426     +904     
Components Coverage Δ
Analytics 70.78% <ø> (ø)
Common 70.88% <ø> (-0.47%) ⬇️
Core 61.30% <45.00%> (-5.80%) ⬇️
SmartStore 73.45% <ø> (ø)
MobileSync 88.84% <ø> (-0.12%) ⬇️
Files with missing lines Coverage Δ
...Login/NativeLogin/NativeLoginManagerInternal.swift 35.40% <100.00%> (+7.56%) ⬆️

... and 34 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions

Copy link
Copy Markdown
TestsPassed ✅SkippedFailed
SalesforceSDKCore iOS ^18 Test Results1018 ran1018 ✅
TestResult
No test annotations available

@JohnsonEricAtSalesforce
JohnsonEricAtSalesforce deleted the fix/W-24044192-basic-auth-base64 branch August 31, 2026 18:55
@JohnsonEricAtSalesforce

Copy link
Copy Markdown
Contributor Author

Superseded by #4161 (recreated from a correctly-named branch).

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