@@ -18,6 +18,7 @@ import FirebaseAppCheckInterop
18
18
import FirebaseAuthInterop
19
19
import FirebaseCore
20
20
import FirebaseCoreExtension
21
+ import FirebaseCoreInternal
21
22
#if COCOAPODS
22
23
internal import GoogleUtilities
23
24
#else
@@ -83,48 +84,75 @@ extension Auth: AuthInterop {
83
84
public func getToken( forcingRefresh forceRefresh: Bool ,
84
85
completion callback: @escaping ( String ? , Error ? ) -> Void ) {
85
86
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) )
116
114
}
115
+ /// Exit here as this path is for rGCIPFirebaseToken only.
116
+ return
117
117
}
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 {
120
148
DispatchQueue . main. async {
121
149
callback ( nil , nil )
122
150
}
123
151
return
124
152
}
125
153
// Call back with current user token.
126
154
currentUser
127
- . internalGetToken ( forceRefresh: forceRefresh, backend: strongSelf . backend) { token, error in
155
+ . internalGetToken ( forceRefresh: forceRefresh, backend: self . backend) { token, error in
128
156
DispatchQueue . main. async {
129
157
callback ( token, error)
130
158
}
@@ -2265,6 +2293,12 @@ extension Auth: AuthInterop {
2265
2293
return { result in
2266
2294
switch result {
2267
2295
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
2268
2302
do {
2269
2303
try self . updateCurrentUser ( authResult. user, byForce: false , savingToDisk: true )
2270
2304
Auth . wrapMainAsync ( callback: callback, with: . success( authResult) )
@@ -2428,9 +2462,20 @@ extension Auth: AuthInterop {
2428
2462
///
2429
2463
/// Mutations should occur within a @synchronized(self) context.
2430
2464
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 )
2431
2475
}
2432
2476
2433
- /// Regionalized auth
2477
+ // MARK: Regionalized auth
2478
+
2434
2479
@available ( iOS 13 , tvOS 13 , macOS 10 . 15 , macCatalyst 13 , watchOS 7 , * )
2435
2480
public extension Auth {
2436
2481
/// Gets the Auth object for a `FirebaseApp` configured for a specific Regional Google Cloud
@@ -2515,6 +2560,13 @@ public extension Auth {
2515
2560
token: response. firebaseToken,
2516
2561
expirationDate: response. expirationDate
2517
2562
)
2563
+ rGCIPFirebaseTokenLock. withLock { token in
2564
+ if self . _currentUser != nil {
2565
+ try ? self . signOut ( )
2566
+ }
2567
+ token = firebaseToken
2568
+ }
2569
+ return firebaseToken
2518
2570
} catch {
2519
2571
throw error
2520
2572
}
0 commit comments