Skip to content

Commit d9dfa0d

Browse files
committed
fixing- use FIRAllocatedUnfairLock type to combine token and its lock
1 parent e0c0562 commit d9dfa0d

File tree

2 files changed

+89
-37
lines changed

2 files changed

+89
-37
lines changed

FirebaseAuth/Sources/Swift/Auth/Auth.swift

Lines changed: 86 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import FirebaseAppCheckInterop
1818
import FirebaseAuthInterop
1919
import FirebaseCore
2020
import FirebaseCoreExtension
21+
import FirebaseCoreInternal
2122
#if COCOAPODS
2223
internal import GoogleUtilities
2324
#else
@@ -83,48 +84,75 @@ extension Auth: AuthInterop {
8384
public func getToken(forcingRefresh forceRefresh: Bool,
8485
completion callback: @escaping (String?, Error?) -> Void) {
8586
kAuthGlobalWorkQueue.async { [weak self] in
86-
if let strongSelf = self {
87-
// Enable token auto-refresh if not already enabled.
88-
if !strongSelf.autoRefreshTokens {
89-
AuthLog.logInfo(code: "I-AUT000002", message: "Token auto-refresh enabled.")
90-
strongSelf.autoRefreshTokens = true
91-
strongSelf.scheduleAutoTokenRefresh()
92-
93-
#if os(iOS) || os(tvOS) // TODO(ObjC): Is a similar mechanism needed on macOS?
94-
strongSelf.applicationDidBecomeActiveObserver =
95-
NotificationCenter.default.addObserver(
96-
forName: UIApplication.didBecomeActiveNotification,
97-
object: nil, queue: nil
98-
) { notification in
99-
if let strongSelf = self {
100-
strongSelf.isAppInBackground = false
101-
if !strongSelf.autoRefreshScheduled {
102-
strongSelf.scheduleAutoTokenRefresh()
103-
}
104-
}
105-
}
106-
strongSelf.applicationDidEnterBackgroundObserver =
107-
NotificationCenter.default.addObserver(
108-
forName: UIApplication.didEnterBackgroundNotification,
109-
object: nil, queue: nil
110-
) { notification in
111-
if let strongSelf = self {
112-
strongSelf.isAppInBackground = true
113-
}
114-
}
115-
#endif
87+
guard let self = self else {
88+
DispatchQueue.main.async { callback(nil, nil) }
89+
return
90+
}
91+
/// Before checking for a standard user, check if we are in a token-only session established
92+
/// by a successful exchangeToken call.
93+
let rGCIPToken = self.rGCIPFirebaseTokenLock.withLock { $0 }
94+
95+
if let token = rGCIPToken {
96+
/// Logic for tokens obtained via exchangeToken (R-GCIP mode)
97+
if token.expirationDate < Date() {
98+
/// Token expired
99+
let error = AuthErrorUtils
100+
.userTokenExpiredError(
101+
message: "The firebase access token obtained via exchangeToken() has expired."
102+
)
103+
Auth.wrapMainAsync(callback: callback, with: .failure(error))
104+
} else if forceRefresh {
105+
/// Token is not expired, but forceRefresh was requested which is currently unsupported
106+
let error = AuthErrorUtils
107+
.operationNotAllowedError(
108+
message: "forceRefresh is not supported for firebase access tokens obtained via exchangeToken()."
109+
)
110+
Auth.wrapMainAsync(callback: callback, with: .failure(error))
111+
} else {
112+
/// The token is valid and not expired.
113+
Auth.wrapMainAsync(callback: callback, with: .success(token.token))
116114
}
115+
/// Exit here as this path is for rGCIPFirebaseToken only.
116+
return
117117
}
118-
// Call back with 'nil' if there is no current user.
119-
guard let strongSelf = self, let currentUser = strongSelf._currentUser else {
118+
/// Fallback to standard `currentUser` logic if not in token-only mode.
119+
/// ... (rest of the original function)
120+
if !self.autoRefreshTokens {
121+
AuthLog.logInfo(code: "I-AUT000002", message: "Token auto-refresh enabled.")
122+
self.autoRefreshTokens = true
123+
self.scheduleAutoTokenRefresh()
124+
125+
#if os(iOS) || os(tvOS)
126+
self.applicationDidBecomeActiveObserver =
127+
NotificationCenter.default.addObserver(
128+
forName: UIApplication.didBecomeActiveNotification,
129+
object: nil,
130+
queue: nil
131+
) { [weak self] _ in
132+
guard let self = self, !self.isAppInBackground,
133+
!self.autoRefreshScheduled else { return }
134+
self.scheduleAutoTokenRefresh()
135+
}
136+
self.applicationDidEnterBackgroundObserver =
137+
NotificationCenter.default.addObserver(
138+
forName: UIApplication.didEnterBackgroundNotification,
139+
object: nil,
140+
queue: nil
141+
) { [weak self] _ in
142+
self?.isAppInBackground = true
143+
}
144+
#endif
145+
}
146+
147+
guard let currentUser = self._currentUser else {
120148
DispatchQueue.main.async {
121149
callback(nil, nil)
122150
}
123151
return
124152
}
125153
// Call back with current user token.
126154
currentUser
127-
.internalGetToken(forceRefresh: forceRefresh, backend: strongSelf.backend) { token, error in
155+
.internalGetToken(forceRefresh: forceRefresh, backend: self.backend) { token, error in
128156
DispatchQueue.main.async {
129157
callback(token, error)
130158
}
@@ -2265,6 +2293,12 @@ extension Auth: AuthInterop {
22652293
return { result in
22662294
switch result {
22672295
case let .success(authResult):
2296+
/// When a standard user successfully signs in, any existing token-only session must be
2297+
/// invalidated to prevent a conflicting auth state.
2298+
/// Clear any R-GCIP session state when a standard user signs in. This ensures we exit
2299+
/// Token-Only Mode.
2300+
self.rGCIPFirebaseTokenLock.withLock { $0 = nil }
2301+
/// ... rest of original function
22682302
do {
22692303
try self.updateCurrentUser(authResult.user, byForce: false, savingToDisk: true)
22702304
Auth.wrapMainAsync(callback: callback, with: .success(authResult))
@@ -2428,9 +2462,20 @@ extension Auth: AuthInterop {
24282462
///
24292463
/// Mutations should occur within a @synchronized(self) context.
24302464
private var listenerHandles: NSMutableArray = []
2465+
2466+
// R-GCIP Token-Only Session State
2467+
2468+
/// The session token obtained from a successful `exchangeToken` call, protected by a lock.
2469+
///
2470+
/// This property is used to support a "token-only" authentication mode for Regionalized
2471+
/// GCIP, where no `User` object is created. It is mutually exclusive with `_currentUser`.
2472+
/// If the wrapped value is non-nil, the `AuthInterop` layer will use it for token generation
2473+
/// instead of relying on a `currentUser`.
2474+
private var rGCIPFirebaseTokenLock = FIRAllocatedUnfairLock<FirebaseToken?>(initialState: nil)
24312475
}
24322476

2433-
/// Regionalized auth
2477+
// MARK: Regionalized auth
2478+
24342479
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
24352480
public extension Auth {
24362481
/// Gets the Auth object for a `FirebaseApp` configured for a specific Regional Google Cloud
@@ -2515,6 +2560,13 @@ public extension Auth {
25152560
token: response.firebaseToken,
25162561
expirationDate: response.expirationDate
25172562
)
2563+
rGCIPFirebaseTokenLock.withLock { token in
2564+
if self._currentUser != nil {
2565+
try? self.signOut()
2566+
}
2567+
token = firebaseToken
2568+
}
2569+
return firebaseToken
25182570
} catch {
25192571
throw error
25202572
}

FirebaseAuth/Sources/Swift/Backend/RPC/IdentityPlatform/ExchangeTokenResponse.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,12 @@ struct ExchangeTokenResponse: AuthRPCResponse {
4242
guard let token = dictionary["accessToken"] as? String else {
4343
throw AuthErrorUtils.unexpectedResponse(deserializedResponse: dictionary)
4444
}
45-
self.firebaseToken = token
45+
firebaseToken = token
4646
guard let expiresInString = dictionary["expiresIn"] as? String,
4747
let expiresInInterval = TimeInterval(expiresInString) else {
4848
throw AuthErrorUtils.unexpectedResponse(deserializedResponse: dictionary)
4949
}
50-
self.expiresIn = expiresInInterval
51-
self.expirationDate = Date().addingTimeInterval(expiresIn)
50+
expiresIn = expiresInInterval
51+
expirationDate = Date().addingTimeInterval(expiresIn)
5252
}
5353
}

0 commit comments

Comments
 (0)