You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Port forcedotcom#4098: fix nil-sceneId crash on advanced-auth browser callback (unit 43/49)
Semantic re-implementation FROM forcedotcom/dev e4e8388 INTO the ObjC->Swift
migration branch. OAuth advanced-auth (ASWebAuthenticationSession) scene fix.
When advanced-auth login starts before any UIScene has connected (cold launch),
or the weak authSession deallocates before the browser callback fires, sceneId
was nil and got inserted into the URL-handler options dictionary, crashing on
the nil insert and dropping the session from the authSessions store.
Ported into the compiled Swift twins:
- SFSDKAuthSession.swift: synthesize a unique per-session scene id
(com.salesforce.mobilesdk.unscopedAuthSession-<UUID>) when no scene is
connected. This also fixes a latent collision in the migration, where the
non-optional sceneId defaulted to "" and all scene-less sessions shared one
authSessions[] key.
- SFOAuthCoordinator.swift: browserCallbackOptions(forSceneId:) omits the key
when the id is nil (empty dict) instead of inserting nil; the URL handler then
falls back to the default scene. Helper is internal (visible to @testable),
no new public API.
De-referenced .m files (SFSDKAuthSession.m, SFOAuthCoordinator.m,
SFOAuthCoordinatorTests.m) ref-synced byte-faithful to upstream for clean future
merges; SFOAuthCoordinator+Internal.h is a migration tombstone (skip). The 4 new
tests were also ported to the compiled Swift twin SFOAuthCoordinatorTests.swift.
SDKCore/SmartStore/MobileSync TEST BUILD SUCCEEDED (0 errors, 0 new warnings);
6 SFOAuthCoordinatorTests pass. Marker advanced 42->43 (e4e8388).
Escalation: OAuth advanced-auth/scene callback behavior -- flag in PR.
| 40 | #4088 invalid login-host recovery | a2a271cca | B | ⚠ login-host + L10n(pre-appr) | ✅ ported 6263a436e4127e450592d5be35410ecbce8d9f53 | Validate login-host input; recover to previous host when the current one fails. Ported into compiled Swift twins: **NewLoginHostView.swift** (per-field inline error UI: `NewLoginHostField.errorMessage`/`errorAccessibilityID`; `save(...)` validates non-empty + contains `.` + no whitespace + parseable `https://` URL, sets `hostError`=`LOGIN_INVALID_HOST` and bails on invalid; `.onChange(of: host)` clears error) — twin diverged from pre-image only in 2 `init` API-parity lines (outside change regions) so applied hunks surgically. **SFUserAccountManager.swift** (new `internal var previousLoginHost`; set in `hostListViewController(_:didChange:)`; recovery rewrite in `hostConnectionErrorHandlerBlock` completion: only auto-remove a *deletable* failing host on a **strong-bad-host** signal [`kSFOAuthErrorInvalidURL` / `NSURLErrorBadURL`/`UnsupportedURL`/`AppTransportSecurityRequiresSecureConnection`], NOT on ambiguous DNS/timeout [captive-portal safety]; recovery host = `previousLoginHost` if still in storage else `loginHost(at:0)` guarded by `numberOfLoginHosts>0` to avoid range trap on empty storage). L10n `LOGIN_INVALID_HOST` (pre-approved). Test-visibility: relaxed `SFSDKLoginHostStorage.loginHostList` `private`→`internal` (mirrors ObjC test's KVC reach into the private ivar; no new public API). De-ref ref-sync: `SFUserAccountManager+Internal.h` — added `previousLoginHost` @property to the `#if !defined(SWIFT_CLASS)` ObjC class-extension region verbatim (anchor `nativeLoginEnabled`→`setCurrentUserInternal` matched pre-image; header has NO compiled consumer — all 7 importer `.m` de-referenced — but kept in sync for clean future merges). Stub SKIP (no matching region): `SFUserAccountManager.m` (79-line stub). Tests: NEW ObjC `SFUserAccountManagerLoginHostRecoveryTests.m` (357 lines, `method_exchangeImplementations` swizzle + KVC) ported to a **Swift twin** `SFUserAccountManagerLoginHostRecoveryTests.swift` (9 tests: previousLoginHost capture, restore-to-previous, index-0 fallback ×2, strong-signal removal, ambiguous/DNS keep, empty-storage guard, non-deletable keep) — swizzles `restartAuthentication:` via `method_exchangeImplementations` (mirrors `SFSDKLogoutBlocker.swift`), snapshots the now-`internal` `loginHostList` for the empty-storage case, and attaches the running app's `UIScene` to the request (the migrated `showErrorAlert` skips presentation on nil scene, unlike upstream ObjC — this is why the first test run timed out; fix = set `request.scene`); wired into pbxproj as **Swift** (2 fresh IDs `E40A1C01/02…`), NOT ObjC. `NewLoginHostTests.swift` +6 validation tests (verbatim, matched pre-image). SDKCore/SmartStore/MobileSync TEST BUILD ✓ (0 errors, 0 new warnings in diff); 19 SDKCore tests PASS (10 NewLoginHost + 9 recovery, fixture-based/not live-gated). ⚠ escalation: login-UI + login-host recovery behavior + L10n — flag in PR. |
74
74
| 41 | #4095 token-exchange error tests | e4bdf6397 | A | — | ✅ ported fd0fde64acb2d2a1ba828deffa7262a5449facfd | OAuth2 token-endpoint error classification tests. NEW **SFSDKOAuth2TokenExchangeErrorTests.swift** (7 tests: invalid_grant family ×3 descriptions, invalid_client_id, invalid_client, unsupported_grant_type, invalid_request, enum-mapping lock-in for the 5 wire values, success-response control) ported byte-faithful except two migration adaptations forced by the unit-38 Swift port: (1) `SFSDKOAuthTokenEndpointErrorResponse.errorCode` is the **typed `SFOAuthErrorCode` enum** in the Swift twin (was `NSInteger` in ObjC), so the assertion compares enum-to-enum (`expectedEnum`) instead of `expectedEnum.rawValue`; (2) the Swift `SFSDKOAuthTokenEndpointResponse(dictionary:parseAdditionalFields:)` init is **non-failable** and takes **`NSDictionary`** (was an implicitly-optional ObjC init), so call sites bind directly (no `guard let`) with `let params: NSDictionary`. Wired into SDKCore-test pbxproj as Swift (2 fresh IDs `E40A1C03/04…`). Test-only, SDKCore-scoped — no production change. SDKCore TEST BUILD ✓ (0 errors, 0 new warnings); 7 tests PASS. No escalation. |
75
75
| 42 | #4096 SQLCipher 4.17.0 | 303013dd7 | C | ⚠⚠ dependency bump (PRE-APPROVED gate) | ✅ ported 2e54cc1c8d37293aa0514f9d9fda7fd101e06df8 | SQLCipher **4.16.0 → 4.17.0** (SQLite 3.53.1 → **3.53.3** runtime). Mechanical version bump across 7 files: **SmartStore.podspec** (`smartstore.dependency 'SQLCipher', '~> 4.17.0'`), **mobilesdk_pods.rb** (`pod 'SQLCipher', '4.17.0'`), **SmartStore pbxproj** + **MobileSyncExplorer pbxproj** (SPM `XCRemoteSwiftPackageReference` exactVersion `4.17.0`, tab-indented, single occurrence each, `plutil` clean), **SFSmartStoreTests.swift** (the COMPILED twin — `testSqliteVersion` `3.53.3`, `testSqlCipherVersion` `4.17.0 community`) + de-ref **SFSmartStoreTests.m** ref-synced verbatim (`.m` is 0-in-Sources; `.swift` twin at pbxproj line 856 is compiled), **`.claude/skills/update-sqlcipher/SKILL.md`** (byte-identical to upstream post-image `79b7335f`: `SQLLite`→`SQLite` typo + example-placeholder assert → `NEW_SQLITE_VERSION`). SPM resolved+checked-out 4.17.0; a stale precompiled-module cache (`sqlite3.h` changed under the version swap) forced a **full Build-dir wipe** (SourcePackages retained) — clean rebuild GREEN. SmartStore + MobileSync (top-of-chain) TEST BUILD ✓ 0 errors; 3 version tests PASS (runtime confirmed `4.17.0 community` / SQLite `3.53.3`). ⚠⚠ dependency bump — PRE-APPROVED (Feedback #4), still flag in PR. |
| 43 | #4098 fix nil-sceneId crash on advanced-auth browser callback | e4e838863 | B | ⚠ OAuth/scene | ✅ ported acf8ccf568316871a03b2a026ffa7f6816b709f6 | Advanced-auth (ASWebAuthenticationSession) browser callback crashed when the login started before any UIScene connected: `sceneId` was nil (`persistentIdentifier` nil pre-scene / weak authSession dealloc'd) and got inserted into the callback options dict. Ported into compiled Swift twins: **SFSDKAuthSession.swift** (new `static let unscopedSceneIdPrefix`; init synthesizes a unique per-session id `"<prefix><UUID>"` when `request.scene?.session.persistentIdentifier` is nil — **also fixes a latent collision the migration had**: migrated `sceneId` was a non-optional `String` defaulting to `""`, so all scene-less sessions previously shared one `authSessions[]` key), **SFOAuthCoordinator.swift** (new `browserCallbackOptions(forSceneId:) -> [AnyHashable:Any]` returns `[:]` on nil else `[UserAccountManager.IDPSceneKey: sceneId]`; callback now calls it — no more empty-string key). Helper is `internal` → visible to `@testable` (no new public API; upstream declared it in `SFOAuthCoordinator+Internal.h`). De-ref ref-sync (byte-faithful to upstream post-image, matched pre-image): `SFSDKAuthSession.m` (prefix const + synthesized `_sceneId`), `SFOAuthCoordinator.m` (`browserCallbackOptionsForSceneId:` + call site). Tombstone SKIP: `SFOAuthCoordinator+Internal.h` (migration tombstone, no `@interface` region — the Swift class holds the real decl). Tests: de-ref `SFOAuthCoordinatorTests.m` +74 ref-synced verbatim (region byte-identical to upstream), AND the 4 tests ported to the compiled Swift twin `SFOAuthCoordinatorTests.swift` (adapted to Swift surface: `SFSDKAuthSession(with:credentials:)`, `SFOAuthCoordinator(authSession:)`, `UserAccountManager.IDPSceneKey`, `sceneId.isEmpty`/`hasPrefix` since non-optional String). SDKCore/SmartStore/MobileSync TEST BUILD ✓ (0 errors, 0 NEW warnings — 2 pre-existing unrelated warnings confirmed unchanged); 6 SFOAuthCoordinatorTests PASS (4 new + 2 existing, not live-gated). ⚠ escalation: OAuth/scene callback behavior — flag in PR. |
0 commit comments