Skip to content
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

[Auth] Get 'currentUser' on Auth worker queue #14141

Merged
merged 2 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions FirebaseAuth/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# Unreleased
- [fixed] Restore Firebase 10 behavior by synchronizing access to the
`Auth.currentUser` API. This resolves some Firebase 11 issues where the
current user is unexpectedly `nil` at startup.

# 11.5.0
- [fixed] Restore pre-Firebase 11 decoding behavior to prevent users getting
logged out when upgrading from Firebase 8.10.0 or earlier to Firebase 11.
Expand Down
46 changes: 26 additions & 20 deletions FirebaseAuth/Sources/Swift/Auth/Auth.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ extension Auth: AuthInterop {
}
}
// Call back with 'nil' if there is no current user.
guard let strongSelf = self, let currentUser = strongSelf.currentUser else {
guard let strongSelf = self, let currentUser = strongSelf._currentUser else {
DispatchQueue.main.async {
callback(nil, nil)
}
Expand All @@ -136,7 +136,7 @@ extension Auth: AuthInterop {
///
/// This method is not for public use. It is for Firebase clients of AuthInterop.
open func getUserID() -> String? {
return currentUser?.uid
return _currentUser?.uid
}
}

Expand Down Expand Up @@ -170,7 +170,13 @@ extension Auth: AuthInterop {
@objc public internal(set) weak var app: FirebaseApp?

/// Synchronously gets the cached current user, or null if there is none.
@objc public internal(set) var currentUser: User?
@objc public var currentUser: User? {
kAuthGlobalWorkQueue.sync {
_currentUser
}
}

private var _currentUser: User?

/// The current user language code.
///
Expand Down Expand Up @@ -702,7 +708,7 @@ extension Auth: AuthInterop {
@objc open func signInAnonymously(completion: ((AuthDataResult?, Error?) -> Void)? = nil) {
kAuthGlobalWorkQueue.async {
let decoratedCallback = self.signInFlowAuthDataResultCallback(byDecorating: completion)
if let currentUser = self.currentUser, currentUser.isAnonymous {
if let currentUser = self._currentUser, currentUser.isAnonymous {
let result = AuthDataResult(withUser: currentUser, additionalUserInfo: nil)
decoratedCallback(result, nil)
return
Expand Down Expand Up @@ -1264,7 +1270,7 @@ extension Auth: AuthInterop {
/// dictionary will contain more information about the error encountered.
@objc(signOut:) open func signOut() throws {
try kAuthGlobalWorkQueue.sync {
guard self.currentUser != nil else {
guard self._currentUser != nil else {
return
}
return try self.updateCurrentUser(nil, byForce: false, savingToDisk: true)
Expand Down Expand Up @@ -1385,14 +1391,14 @@ extension Auth: AuthInterop {
queue: OperationQueue.main
) { notification in
if let auth = notification.object as? Auth {
listener(auth, auth.currentUser)
listener(auth, auth._currentUser)
}
}
objc_sync_enter(Auth.self)
listenerHandles.add(listener)
objc_sync_exit(Auth.self)
DispatchQueue.main.async {
listener(self, self.currentUser)
listener(self, self._currentUser)
}
return handle
}
Expand Down Expand Up @@ -1434,7 +1440,7 @@ extension Auth: AuthInterop {
/// complete, or fails. Invoked asynchronously on the main thread in the future.
@objc open func revokeToken(withAuthorizationCode authorizationCode: String,
completion: ((Error?) -> Void)? = nil) {
currentUser?.internalGetToken(backend: backend) { idToken, error in
_currentUser?.internalGetToken(backend: backend) { idToken, error in
if let error {
Auth.wrapMainAsync(completion, error)
return
Expand Down Expand Up @@ -1790,7 +1796,7 @@ extension Auth: AuthInterop {
}

func updateKeychain(withUser user: User?) -> Error? {
if user != currentUser {
if user != _currentUser {
// No-op if the user is no longer signed in. This is not considered an error as we don't check
// whether the user is still current on other callbacks of user operations either.
return nil
Expand Down Expand Up @@ -1836,7 +1842,7 @@ extension Auth: AuthInterop {
}

func signOutByForce(withUserID userID: String) throws {
guard currentUser?.uid == userID else {
guard _currentUser?.uid == userID else {
return
}
try updateCurrentUser(nil, byForce: true, savingToDisk: true)
Expand All @@ -1846,7 +1852,7 @@ extension Auth: AuthInterop {

/// Posts the auth state change notification if current user's token has been changed.
private func possiblyPostAuthStateChangeNotification() {
let token = currentUser?.rawAccessToken()
let token = _currentUser?.rawAccessToken()
if lastNotifiedUserToken == token ||
(token != nil && lastNotifiedUserToken == token) {
return
Expand All @@ -1863,7 +1869,7 @@ extension Auth: AuthInterop {
if let token, token.count > 0 {
internalNotificationParameters[FIRAuthStateDidChangeInternalNotificationTokenKey] = token
}
internalNotificationParameters[FIRAuthStateDidChangeInternalNotificationUIDKey] = currentUser?
internalNotificationParameters[FIRAuthStateDidChangeInternalNotificationUIDKey] = _currentUser?
.uid
let notifications = NotificationCenter.default
DispatchQueue.main.async {
Expand All @@ -1880,7 +1886,7 @@ extension Auth: AuthInterop {
/// If the token expires in less than 5 minutes, schedule the token refresh immediately.
private func scheduleAutoTokenRefresh() {
let tokenExpirationInterval =
(currentUser?.accessTokenExpirationDate()?.timeIntervalSinceNow ?? 0) - 5 * 60
(_currentUser?.accessTokenExpirationDate()?.timeIntervalSinceNow ?? 0) - 5 * 60
scheduleAutoTokenRefresh(withDelay: max(tokenExpirationInterval, 0), retry: false)
}

Expand All @@ -1889,7 +1895,7 @@ extension Auth: AuthInterop {
/// to be executed.
/// - Parameter retry: Flag to determine whether the invocation is a retry attempt or not.
private func scheduleAutoTokenRefresh(withDelay delay: TimeInterval, retry: Bool) {
guard let accessToken = currentUser?.rawAccessToken() else {
guard let accessToken = _currentUser?.rawAccessToken() else {
return
}
let intDelay = Int(ceil(delay))
Expand All @@ -1908,18 +1914,18 @@ extension Auth: AuthInterop {
guard let strongSelf = weakSelf else {
return
}
guard strongSelf.currentUser?.rawAccessToken() == accessToken else {
guard strongSelf._currentUser?.rawAccessToken() == accessToken else {
// Another auto refresh must have been scheduled, so keep _autoRefreshScheduled unchanged.
return
}
strongSelf.autoRefreshScheduled = false
if strongSelf.isAppInBackground {
return
}
let uid = strongSelf.currentUser?.uid
strongSelf.currentUser?
let uid = strongSelf._currentUser?.uid
strongSelf._currentUser?
.internalGetToken(forceRefresh: true, backend: strongSelf.backend) { token, error in
if strongSelf.currentUser?.uid != uid {
if strongSelf._currentUser?.uid != uid {
return
}
if error != nil {
Expand All @@ -1943,7 +1949,7 @@ extension Auth: AuthInterop {
/// - Parameter saveToDisk: Indicates the method should persist the user data to disk.
func updateCurrentUser(_ user: User?, byForce force: Bool,
savingToDisk saveToDisk: Bool) throws {
if user == currentUser {
if user == _currentUser {
possiblyPostAuthStateChangeNotification()
}
if let user {
Expand All @@ -1960,7 +1966,7 @@ extension Auth: AuthInterop {
}
}
if throwError == nil || force {
currentUser = user
_currentUser = user
possiblyPostAuthStateChangeNotification()
}
if let throwError {
Expand Down
Loading