Skip to content

Commit e66053e

Browse files
Port upstream forcedotcom#4087: centralized token-refresh coordinator (unit 44)
Semantic re-implementation of forcedotcom/dev 6e09678 (merge of bbirman/refreshCoordinator) onto the ObjC→Swift migration branch. WHAT: introduces SFSDKTokenRefreshCoordinator — a process-wide singleton that coalesces concurrent token-refresh requests per credential (keyed by credentials.identifier) so at most one refresh is in-flight at a time. This prevents the double-spend race with single-use (rotating) refresh tokens, where concurrent refreshes would invalidate each other's tokens. Callbacks are delivered on the main queue; background-task protection wraps the refresh. MIGRATION NOTES (no new ObjC; compiled path is Swift): - NEW SFSDKTokenRefreshCoordinator.swift (upstream added .h/.m ObjC) — @objc singleton, wired into pbxproj (framework Sources + Headers-free). NEW SFSDKTokenRefreshCoordinatorTests.swift (upstream .m, +602) ported to Swift and wired to the test target. - SFRestAPI.swift / SFIdentityCoordinator.swift / SFUserAccountManager.swift / UserAccountManager.swift (async refresh) / SFSDKOAuth2.swift / SFSDKTestRequestListener.swift: the compiled Swift twins carry the behavior change (route through the coordinator; SFRestAPI: sessionRefreshInProgress + pendingRequestsBeingProcessed + oauthSessionRefresher collapse to a single refreshCycleActive flag; cleanup now delivers "User logged out" and cancels in-flight tasks; OAuth2 completion no longer double-hops main queue; TestRequestListener spins the run loop instead of a semaphore to avoid a main-thread deadlock now that the coordinator delivers on main). - WebSocketClient.swift: TokenRefreshCoordinator actor renamed WebSocketReconnectCoordinator (it only gates reconnection; token dedup now lives in SFSDKTokenRefreshCoordinator). - Public-API deprecation of SFOAuthSessionRefresher (14.0→15.0): expressed on the Swift members consumers call (@available deprecated), with non-deprecated internal seams (init(internalCredentials:) / refreshSessionInternal) that the coordinator, tests, and mock use so the SDK's own paths stay warning-free — mirrors the unit-39 forceAdvancedAuthenticationInternal precedent. Upstream's ObjC SFSDK_DEPRECATED lives on tombstoned headers, so it has no compiled home. - De-referenced .m/.h mirrors (SFRestAPI.m, SFIdentityCoordinator.m, SFSDKOAuth2.m, SFSDKTestRequestListener.m, SFOAuthSessionRefresher.m/.h and the two test .m) ref-synced to the upstream post-image on top of the migrated pre-image (keeping the migration's @import/-Swift.h deltas); tombstone headers skipped; SFUserAccountManager.m had nothing to sync (refreshCredentials lives in the .swift twin). ESCALATION (flag for human PR review): OAuth/token-refresh control flow + public-API deprecation (SFOAuthSessionRefresher). This is the live-auth unblocker for Phase 2. Gate: SDKCore/SmartStore/MobileSync build-for-testing all GREEN, 0 new warnings; 22 targeted tests pass (11 coordinator + 7 data-task-race + 4 refresher). Live-org auth-util end-to-end tests remain XCTSkip-gated (Phase 2). Upstream: 6e09678 (PR forcedotcom#4087) · marker unit 44 · __U44__
1 parent 9f7b84f commit e66053e

23 files changed

Lines changed: 1424 additions & 246 deletions

.claude/upstream-sync-backlog.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Upstream Sync Backlog Ledger
22

3-
Marker (done floor): `e4e838863` (units 1-43 done) · forcedotcom/dev HEAD (target): `b5d37d807` · **6 units remaining** · Re-seeded: 2026-07-19 (Phase 0 of the resume-porting plan).
3+
Marker (done floor): `6e0967833` (units 1-44 done) · forcedotcom/dev HEAD (target): `b5d37d807` · **5 units remaining** · Re-seeded: 2026-07-19 (Phase 0 of the resume-porting plan).
44

55
> **Direction:** we port changes **FROM** `forcedotcom/dev` **INTO** our ObjC→Swift migration branch
66
> (`feature/objc-to-swift-test-migration`). Each unit is a *semantic re-implementation* against the current
@@ -17,7 +17,7 @@ Marker (done floor): `e4e838863` (units 1-43 done) · forcedotcom/dev HEAD (ta
1717
> non-libs (CI, docs, skills, sample apps). Live progress bar = subject of lead task #9.
1818
1919
## Migration status
20-
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░ 43/49 units done (88%) · libs-production-impacting: 16/21 · Phase 1 porting (units 1-43 ✅)
20+
▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░ 44/49 units done (90%) · libs-production-impacting: 17/21 · Phase 1 porting (units 1-44 ✅)
2121

2222
| Bucket | Count | Notes |
2323
|--------|-------|-------|
@@ -74,7 +74,7 @@ Marker (done floor): `e4e838863` (units 1-43 done) · forcedotcom/dev HEAD (ta
7474
| 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. |
7575
| 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. |
7676
| 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. |
77-
| 44 | #4087 token refresh coordinator | 6e0967833 | B+D | ⚠⚠⚠ OAuth/token (LIVE-AUTH UNBLOCKER) | ⬜ pending | 18 files: NEW `SFSDKTokenRefreshCoordinator.h/.m`, `SFOAuthErrorCode.swift`(via #4094), `SFOAuthSessionRefresher.*`, `SFIdentityCoordinator.m`, `SFRestAPI.m`, `UserAccountManager.swift`, `SFSDKOAuth2.m` + tests. **THIS unblocks the 51 SKIP-gated live-org tests → enables Phase 2.** |
77+
| 44 | #4087 token refresh coordinator | 6e0967833 | B+D | ⚠⚠⚠ OAuth/token (LIVE-AUTH UNBLOCKER) | ✅ ported `62fe9ce145eb0c40a82a9ba24028d1d27e0befef` | NEW `SFSDKTokenRefreshCoordinator.swift` (upstream added ObjC .h/.m; migration rule = no new ObjC → ported as @objc Swift singleton, wired to pbxproj) coalesces concurrent per-credential refreshes (keyed by `credentials.identifier`) so single-use/rotating tokens aren't double-spent; main-queue callback delivery + bg-task guard. NEW `SFSDKTokenRefreshCoordinatorTests.swift` (upstream .m +602 → Swift, 11 tests). Compiled Swift twins carry the behavior: SFRestAPI (3 flags `sessionRefreshInProgress`/`pendingRequestsBeingProcessed`/`oauthSessionRefresher` → single `refreshCycleActive`; `cleanup` now delivers "User logged out" + cancels in-flight tasks; removed `sessionRefresher(for:)`), SFIdentityCoordinator (drop `oauthSessionRefresher`, route via coordinator), SFUserAccountManager.refreshCredentials (route via coordinator), UserAccountManager async `refresh(credentials:)`, SFSDKOAuth2 (drop 3 main-queue completion hops), SFSDKTestRequestListener (semaphore→run-loop spin to avoid main-thread deadlock now that coordinator delivers on main), WebSocketClient (`TokenRefreshCoordinator` actor → `WebSocketReconnectCoordinator`; token dedup moved to the process coordinator). **Public-API deprecation of `SFOAuthSessionRefresher` (14.0→15.0):** `@available(*,deprecated)` on the public init + `refreshSession(withCompletion:error:)` consumers call, plus non-deprecated internal seams `init(internalCredentials:)`/`refreshSessionInternal(...)` used by coordinator/tests/mock → 0 warnings (unit-39 `forceAdvancedAuthenticationInternal` precedent; upstream's ObjC `SFSDK_DEPRECATED` has no compiled home since the header is a tombstone). De-ref `.m`/`.h` mirrors ref-synced (kept migration `@import`/`-Swift.h` deltas); tombstone headers (`SFOAuthSessionRefresher.h/+Internal.h`, `SFIdentityCoordinator+Internal.h`) skipped; `SFUserAccountManager.m` had nothing to sync (method lives in twin). **KEY LESSON:** `OAuthCredentials(identifier:clientId:encrypted:)!` returns NIL for keychain storage (unit-36) → coordinator tests MUST use `OAuthCredentials.credentials(identifier:...)` factory (3 call sites; first run failed on nil creds). Gate: SDKCore/SmartStore/MobileSync build ✓ 0 new warn; 22 tests PASS (11 coordinator + 7 data-task-race + 4 refresher). ⚠ ESCALATION (OAuth/token + public-API deprecation) → flag in PR. **UNLOCKS Phase 2** (51 SKIP-gated live-org tests). |
7878
| 45 | #4102 improve token-refresh error handling | 19d4436ab | B | ⚠ OAuth/token | ⬜ pending | `SFRestAPI.m`, `SFSDKOAuth2.h/.m` + tests + pbxproj. |
7979
| 46 | #4105 fix iOS26 login-host classifier | b155f785d | B | ⚠ login-host | ⬜ pending | `SFOAuthCoordinator.m`, `SFSDKAuthErrorManager.m/+Internal.h` + test + pbxproj. |
8080
| 47 | #4103 MobileSync docs | 5c31fb1eb | F || ⬜ pending | docs only. |

.claude/upstream-sync-marker

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
e4e8388636da5c6f993c8bfeb68214d7f74bb331
1+
6e0967833f8b311f7e0483d12bf7116f5c9e43a9

libs/SalesforceSDKCore/SalesforceSDKCore.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@
119119
4F3139682331C5C7007B3705 /* SFSDKAuthRootController.h in Headers */ = {isa = PBXBuildFile; fileRef = 4F3139672331C5B9007B3705 /* SFSDKAuthRootController.h */; };
120120
4F5727E327F27F1A0008CDA4 /* SFSDKPrimingRecordsResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 4F5727DC27F27F1A0008CDA4 /* SFSDKPrimingRecordsResponse.h */; settings = {ATTRIBUTES = (Public, ); }; };
121121
4F5A49502E98711600C89DDD /* ScopeParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F5A494F2E98711600C89DDD /* ScopeParser.swift */; };
122+
FE44A0440000000000000002 /* SFSDKTokenRefreshCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE44A0440000000000000001 /* SFSDKTokenRefreshCoordinator.swift */; };
123+
FE44A0440000000000000004 /* SFSDKTokenRefreshCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE44A0440000000000000003 /* SFSDKTokenRefreshCoordinatorTests.swift */; };
122124
4FOAUTHEC012E98711600C89DDD /* SFOAuthErrorCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FOAUTHEC002E98711600C89DDD /* SFOAuthErrorCode.swift */; };
123125
4FOAUTHECT012E98711600C89DDD /* SFOAuthErrorCodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FOAUTHECT002E98711600C89DDD /* SFOAuthErrorCodeTests.swift */; };
124126
E40A1C032DD7E0AD00138888 /* SFSDKOAuth2TokenExchangeErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E40A1C042DD7E0AD00138888 /* SFSDKOAuth2TokenExchangeErrorTests.swift */; };
@@ -658,6 +660,8 @@
658660
4F5727DC27F27F1A0008CDA4 /* SFSDKPrimingRecordsResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SFSDKPrimingRecordsResponse.h; sourceTree = "<group>"; };
659661
4F5727E227F27F1A0008CDA4 /* SFSDKPrimingRecordsResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SFSDKPrimingRecordsResponse.m; sourceTree = "<group>"; };
660662
4F5A494F2E98711600C89DDD /* ScopeParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScopeParser.swift; sourceTree = "<group>"; };
663+
FE44A0440000000000000001 /* SFSDKTokenRefreshCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SFSDKTokenRefreshCoordinator.swift; sourceTree = "<group>"; };
664+
FE44A0440000000000000003 /* SFSDKTokenRefreshCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SFSDKTokenRefreshCoordinatorTests.swift; path = SalesforceSDKCoreTests/SFSDKTokenRefreshCoordinatorTests.swift; sourceTree = SOURCE_ROOT; };
661665
4FOAUTHEC002E98711600C89DDD /* SFOAuthErrorCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SFOAuthErrorCode.swift; sourceTree = "<group>"; };
662666
4FOAUTHECT002E98711600C89DDD /* SFOAuthErrorCodeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SFOAuthErrorCodeTests.swift; path = SalesforceSDKCoreTests/SFOAuthErrorCodeTests.swift; sourceTree = SOURCE_ROOT; };
663667
E40A1C042DD7E0AD00138888 /* SFSDKOAuth2TokenExchangeErrorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = SFSDKOAuth2TokenExchangeErrorTests.swift; path = SalesforceSDKCoreTests/SFSDKOAuth2TokenExchangeErrorTests.swift; sourceTree = SOURCE_ROOT; };
@@ -1269,6 +1273,7 @@
12691273
B7A901BD228E4DFA0036D749 /* SFSDKLogoutBlocker.m */,
12701274
399A11508BCB47F490DFB724 /* SFOAuthCoordinatorLightningURLTests.swift */,
12711275
4FOAUTHECT002E98711600C89DDD /* SFOAuthErrorCodeTests.swift */,
1276+
FE44A0440000000000000003 /* SFSDKTokenRefreshCoordinatorTests.swift */,
12721277
E40A1C042DD7E0AD00138888 /* SFSDKOAuth2TokenExchangeErrorTests.swift */,
12731278
2B4074A2C0DE44F19A0E3344 /* SFSDKOAuth2RefreshInstanceUrlTests.swift */,
12741279
4F9E052C2DD6A06F00548985 /* SFSDKOAuthTokenEndpointResponseTests.m */,
@@ -1421,6 +1426,7 @@
14211426
4F96FCC41BFD32130022F021 /* OAuth */ = {
14221427
isa = PBXGroup;
14231428
children = (
1429+
FE44A0440000000000000001 /* SFSDKTokenRefreshCoordinator.swift */,
14241430
4F5A494F2E98711600C89DDD /* ScopeParser.swift */,
14251431
23D96B6E2E145AC20004B06A /* DomainDiscoveryCoordinator.swift */,
14261432
4F8A3B002CEC202F00ECDC76 /* JwtAccessToken.swift */,
@@ -2538,6 +2544,7 @@
25382544
4FA1B2C32F0E000000000001 /* LoginForAdminTests.swift in Sources */,
25392545
1A31073F5F374B9EB1162F2E /* SFOAuthCoordinatorLightningURLTests.swift in Sources */,
25402546
4FOAUTHECT012E98711600C89DDD /* SFOAuthErrorCodeTests.swift in Sources */,
2547+
FE44A0440000000000000004 /* SFSDKTokenRefreshCoordinatorTests.swift in Sources */,
25412548
E40A1C032DD7E0AD00138888 /* SFSDKOAuth2TokenExchangeErrorTests.swift in Sources */,
25422549
2B4074A1C0DE44F19A0E1122 /* SFSDKOAuth2RefreshInstanceUrlTests.swift in Sources */,
25432550
237C186C2E44FCAE0008015C /* EncryptStreamTests.swift in Sources */,
@@ -2661,6 +2668,7 @@
26612668
CE4CE3961C0E526A009F6029 /* SFUserAccountManager.m in Sources */,
26622669
A3C7476129F709EB00D72B7F /* BiometricAuthenticationManagerInternal.swift in Sources */,
26632670
4F5A49502E98711600C89DDD /* ScopeParser.swift in Sources */,
2671+
FE44A0440000000000000002 /* SFSDKTokenRefreshCoordinator.swift in Sources */,
26642672
4FOAUTHEC012E98711600C89DDD /* SFOAuthErrorCode.swift in Sources */,
26652673
23945B712D78E4A60060B195 /* NotificationType.swift in Sources */,
26662674
D3B1519F2E1DA6EB00914EF1 /* AuthCoordinatorFrontdoorBridgeLoginOverride.swift in Sources */,

libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Extensions/UserAccountManager.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,18 @@ extension UserAccountManager: UserAccountManaging {
8989
})
9090
}
9191

92+
/// Refresh the session for the given credentials.
93+
/// - Parameter credentials: The credentials to refresh.
94+
/// - Returns: A tuple of the updated `UserAccount` and `SFOAuthInfo` on success.
95+
/// - Throws: `UserAccountManagerError.refreshFailed` if the refresh fails.
96+
public func refresh(credentials: OAuthCredentials) async throws -> (UserAccount, SFOAuthInfo) {
97+
try await withCheckedThrowingContinuation { continuation in
98+
_ = self.refresh(credentials: credentials) { result in
99+
continuation.resume(with: result.mapError { $0 })
100+
}
101+
}
102+
}
103+
92104
/// Switch to a new user. Kicks off the login flow. Once complete switches to a new user on success else does not change the current user.
93105
/// - Parameter completionBlock: completion block to invoke with a UserAccount on success or UserAccountManagerError on failure wrapped in a Result type.
94106
public func switchToNewUserAccount(_ completionBlock: @escaping (Result<UserAccount, UserAccountManagerError>) -> Void) {

libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Identity/SFIdentityCoordinator.m

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
@import SalesforceSDKCommon;
2626
#import "SFIdentityCoordinator+Internal.h"
2727
#import "SFOAuthCredentials.h"
28-
#import "SFOAuthSessionRefresher.h"
28+
#import "SFSDKTokenRefreshCoordinator.h"
2929
#import "SFUserAccountManager.h"
3030
#import "SFNetwork.h"
3131
#import "SFSDKAuthSession.h"
@@ -66,7 +66,6 @@ @implementation SFIdentityCoordinator
6666
@synthesize timeout = _timeout;
6767
@synthesize retrievingData = _retrievingData;
6868
@synthesize session = _session;
69-
@synthesize oauthSessionRefresher = _oauthSessionRefresher;
7069

7170
#pragma mark - init / dealloc
7271

@@ -171,8 +170,7 @@ - (void)sendRequest
171170
if (statusCode == 401 || statusCode == 403) {
172171
// The session timed out. Identity service tends to send 403s for session timeouts. Try to refresh.
173172
[SFSDKCoreLogger i:[self class] format:@"%@: Identity request failed due to expired credentials. Attempting to refresh credentials.", NSStringFromSelector(_cmd)];
174-
strongSelf.oauthSessionRefresher = [[SFOAuthSessionRefresher alloc] initWithCredentials:strongSelf.credentials];
175-
[strongSelf.oauthSessionRefresher refreshSessionWithCompletion:^(SFOAuthCredentials *updatedCredentials) {
173+
[[SFSDKTokenRefreshCoordinator sharedInstance] refreshSessionForCredentials:strongSelf.credentials completion:^(SFOAuthCredentials *updatedCredentials) {
176174
[SFSDKCoreLogger d:[strongSelf class] format:@"%@: Credentials refresh successful. Replaying original identity request.", NSStringFromSelector(_cmd)];
177175
strongSelf.credentials = updatedCredentials;
178176
dispatch_async(dispatch_get_main_queue(), ^{
@@ -221,15 +219,13 @@ - (void)dealloc
221219
self.session = nil;
222220
self.credentials = nil;
223221
self.idData = nil;
224-
self.oauthSessionRefresher = nil;
225222
}
226223

227224
- (void)cleanupData
228225
{
229226
[SFNetwork removeSharedInstanceForIdentifier:self.networkIdentifier];
230227
self.networkIdentifier = nil;
231228
self.session = nil;
232-
self.oauthSessionRefresher = nil;
233229
self.retrievingData = NO;
234230
}
235231

libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Identity/SFIdentityCoordinator.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ public class SFIdentityCoordinator: NSObject {
7272
// Internal properties
7373
var retrievingData: Bool = false
7474
var session: URLSession?
75-
var oauthSessionRefresher: SFOAuthSessionRefresher?
7675
weak var authSession: SFSDKAuthSession?
7776
private var networkIdentifier: String?
7877

@@ -173,8 +172,11 @@ public class SFIdentityCoordinator: NSObject {
173172
let statusCode = (response as? HTTPURLResponse)?.statusCode ?? 0
174173
if statusCode == 401 || statusCode == 403 {
175174
SFSDKCoreLogger.i(Self.self, format: "%@: Identity request failed due to expired credentials. Attempting to refresh credentials.", #function)
176-
self.oauthSessionRefresher = SFOAuthSessionRefresher(credentials: self.credentials)
177-
self.oauthSessionRefresher?.refreshSession(withCompletion: { [weak self] updatedCredentials in
175+
guard let credentials = self.credentials else {
176+
self.notifyDelegateOfFailure(self.error(withType: kSFIdentityErrorTypeBadHttpResponse, description: "Cannot refresh: no credentials."))
177+
return
178+
}
179+
SFSDKTokenRefreshCoordinator.shared.refreshSession(forCredentials: credentials, completion: { [weak self] updatedCredentials in
178180
guard let self = self else { return }
179181
SFSDKCoreLogger.d(Self.self, format: "%@: Credentials refresh successful. Replaying original identity request.", #function)
180182
self.credentials = updatedCredentials
@@ -217,7 +219,6 @@ public class SFIdentityCoordinator: NSObject {
217219
}
218220
networkIdentifier = nil
219221
session = nil
220-
oauthSessionRefresher = nil
221222
retrievingData = false
222223
}
223224

0 commit comments

Comments
 (0)