-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Implement R-GCIP Token-Only Session via ExchangeToken
#14986
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -18,6 +18,7 @@ | |||||
| import FirebaseAuthInterop | ||||||
| import FirebaseCore | ||||||
| import FirebaseCoreExtension | ||||||
| import FirebaseCoreInternal | ||||||
| #if COCOAPODS | ||||||
| internal import GoogleUtilities | ||||||
| #else | ||||||
|
|
@@ -83,48 +84,75 @@ | |||||
| public func getToken(forcingRefresh forceRefresh: Bool, | ||||||
| completion callback: @escaping (String?, Error?) -> Void) { | ||||||
| kAuthGlobalWorkQueue.async { [weak self] in | ||||||
| if let strongSelf = self { | ||||||
| // Enable token auto-refresh if not already enabled. | ||||||
| if !strongSelf.autoRefreshTokens { | ||||||
| AuthLog.logInfo(code: "I-AUT000002", message: "Token auto-refresh enabled.") | ||||||
| strongSelf.autoRefreshTokens = true | ||||||
| strongSelf.scheduleAutoTokenRefresh() | ||||||
|
|
||||||
| #if os(iOS) || os(tvOS) // TODO(ObjC): Is a similar mechanism needed on macOS? | ||||||
| strongSelf.applicationDidBecomeActiveObserver = | ||||||
| NotificationCenter.default.addObserver( | ||||||
| forName: UIApplication.didBecomeActiveNotification, | ||||||
| object: nil, queue: nil | ||||||
| ) { notification in | ||||||
| if let strongSelf = self { | ||||||
| strongSelf.isAppInBackground = false | ||||||
| if !strongSelf.autoRefreshScheduled { | ||||||
| strongSelf.scheduleAutoTokenRefresh() | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| strongSelf.applicationDidEnterBackgroundObserver = | ||||||
| NotificationCenter.default.addObserver( | ||||||
| forName: UIApplication.didEnterBackgroundNotification, | ||||||
| object: nil, queue: nil | ||||||
| ) { notification in | ||||||
| if let strongSelf = self { | ||||||
| strongSelf.isAppInBackground = true | ||||||
| } | ||||||
| } | ||||||
| #endif | ||||||
| guard let self = self else { | ||||||
| DispatchQueue.main.async { callback(nil, nil) } | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would using any of the |
||||||
| return | ||||||
| } | ||||||
| /// Before checking for a standard user, check if we are in a token-only session established | ||||||
| /// by a successful exchangeToken call. | ||||||
| let rGCIPToken = self.rGCIPFirebaseTokenLock.withLock { $0 } | ||||||
|
|
||||||
| if let token = rGCIPToken { | ||||||
| /// Logic for tokens obtained via exchangeToken (R-GCIP mode) | ||||||
| if token.expirationDate < Date() { | ||||||
| /// Token expired | ||||||
| let error = AuthErrorUtils | ||||||
| .userTokenExpiredError( | ||||||
| message: "The firebase access token obtained via exchangeToken() has expired." | ||||||
| ) | ||||||
| Auth.wrapMainAsync(callback: callback, with: .failure(error)) | ||||||
| } else if forceRefresh { | ||||||
| /// Token is not expired, but forceRefresh was requested which is currently unsupported | ||||||
| let error = AuthErrorUtils | ||||||
| .operationNotAllowedError( | ||||||
| message: "forceRefresh is not supported for firebase access tokens obtained via exchangeToken()." | ||||||
| ) | ||||||
| Auth.wrapMainAsync(callback: callback, with: .failure(error)) | ||||||
| } else { | ||||||
| /// The token is valid and not expired. | ||||||
| Auth.wrapMainAsync(callback: callback, with: .success(token.token)) | ||||||
| } | ||||||
| /// Exit here as this path is for rGCIPFirebaseToken only. | ||||||
| return | ||||||
| } | ||||||
| // Call back with 'nil' if there is no current user. | ||||||
| guard let strongSelf = self, let currentUser = strongSelf._currentUser else { | ||||||
| /// Fallback to standard `currentUser` logic if not in token-only mode. | ||||||
| /// ... (rest of the original function) | ||||||
| if !self.autoRefreshTokens { | ||||||
| AuthLog.logInfo(code: "I-AUT000002", message: "Token auto-refresh enabled.") | ||||||
| self.autoRefreshTokens = true | ||||||
| self.scheduleAutoTokenRefresh() | ||||||
|
|
||||||
| #if os(iOS) || os(tvOS) | ||||||
| self.applicationDidBecomeActiveObserver = | ||||||
| NotificationCenter.default.addObserver( | ||||||
| forName: UIApplication.didBecomeActiveNotification, | ||||||
| object: nil, | ||||||
| queue: nil | ||||||
| ) { [weak self] _ in | ||||||
| guard let self = self, !self.isAppInBackground, | ||||||
| !self.autoRefreshScheduled else { return } | ||||||
| self.scheduleAutoTokenRefresh() | ||||||
| } | ||||||
| self.applicationDidEnterBackgroundObserver = | ||||||
| NotificationCenter.default.addObserver( | ||||||
| forName: UIApplication.didEnterBackgroundNotification, | ||||||
| object: nil, | ||||||
| queue: nil | ||||||
| ) { [weak self] _ in | ||||||
| self?.isAppInBackground = true | ||||||
| } | ||||||
| #endif | ||||||
| } | ||||||
|
|
||||||
| guard let currentUser = self._currentUser else { | ||||||
| DispatchQueue.main.async { | ||||||
| callback(nil, nil) | ||||||
| } | ||||||
| return | ||||||
| } | ||||||
| // Call back with current user token. | ||||||
| currentUser | ||||||
| .internalGetToken(forceRefresh: forceRefresh, backend: strongSelf.backend) { token, error in | ||||||
| .internalGetToken(forceRefresh: forceRefresh, backend: self.backend) { token, error in | ||||||
| DispatchQueue.main.async { | ||||||
| callback(token, error) | ||||||
| } | ||||||
|
|
@@ -2265,6 +2293,12 @@ | |||||
| return { result in | ||||||
| switch result { | ||||||
| case let .success(authResult): | ||||||
| /// When a standard user successfully signs in, any existing token-only session must be | ||||||
| /// invalidated to prevent a conflicting auth state. | ||||||
| /// Clear any R-GCIP session state when a standard user signs in. This ensures we exit | ||||||
| /// Token-Only Mode. | ||||||
| self.rGCIPFirebaseTokenLock.withLock { $0 = nil } | ||||||
| /// ... rest of original function | ||||||
srushtisv marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| do { | ||||||
| try self.updateCurrentUser(authResult.user, byForce: false, savingToDisk: true) | ||||||
| Auth.wrapMainAsync(callback: callback, with: .success(authResult)) | ||||||
|
|
@@ -2428,9 +2462,20 @@ | |||||
| /// | ||||||
| /// Mutations should occur within a @synchronized(self) context. | ||||||
| private var listenerHandles: NSMutableArray = [] | ||||||
|
|
||||||
| // R-GCIP Token-Only Session State | ||||||
|
|
||||||
| /// The session token obtained from a successful `exchangeToken` call, protected by a lock. | ||||||
| /// | ||||||
| /// This property is used to support a "token-only" authentication mode for Regionalized | ||||||
| /// GCIP, where no `User` object is created. It is mutually exclusive with `_currentUser`. | ||||||
| /// If the wrapped value is non-nil, the `AuthInterop` layer will use it for token generation | ||||||
| /// instead of relying on a `currentUser`. | ||||||
| private var rGCIPFirebaseTokenLock = FIRAllocatedUnfairLock<FirebaseToken?>(initialState: nil) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| } | ||||||
|
|
||||||
| /// Regionalized auth | ||||||
| // MARK: Regionalized auth | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not: the added dash adds a horizontal line divider in Xcode
Suggested change
|
||||||
|
|
||||||
| @available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *) | ||||||
| public extension Auth { | ||||||
| /// Gets the Auth object for a `FirebaseApp` configured for a specific Regional Google Cloud | ||||||
|
|
@@ -2515,6 +2560,13 @@ | |||||
| token: response.firebaseToken, | ||||||
| expirationDate: response.expirationDate | ||||||
| ) | ||||||
| rGCIPFirebaseTokenLock.withLock { token in | ||||||
| if self._currentUser != nil { | ||||||
| try? self.signOut() | ||||||
| } | ||||||
| token = firebaseToken | ||||||
|
Check failure on line 2567 in FirebaseAuth/Sources/Swift/Auth/Auth.swift
|
||||||
| } | ||||||
| return firebaseToken | ||||||
|
Check failure on line 2569 in FirebaseAuth/Sources/Swift/Auth/Auth.swift
|
||||||
| } catch { | ||||||
| throw error | ||||||
| } | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.