Repository navigation
Conversation
…cally When a request or token refresh failed with userNotFound, userDisabled, invalidUserToken or userTokenExpired, Auth signed the user out and only posted authStateDidChangeNotification, the same as for signOut(). Apps couldn't tell why the user was signed out unless their own call had failed. Refreshes started by other Firebase SDKs or by the automatic token refresh gave them no error at all. Post Auth.automaticSignOutNotification after the sign-out, with the error and the user ID in userInfo. signOut() and User.delete() don't post it. Fixes #16855
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. |
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces Auth.automaticSignOutNotification and its associated keys, which are posted when a user is automatically signed out due to backend errors. It updates the automatic sign-out flow to use the new signOutAutomatically method and adds unit tests to verify this behavior. Feedback highlights a thread-safety issue in signOutAutomatically(withUserID:error:) where _currentUser and updateCurrentUser are accessed outside of the serial kAuthGlobalWorkQueue, potentially causing data races. It is recommended to wrap this logic in kAuthGlobalWorkQueue.async as suggested.
Run `signOutAutomatically(withUserID:error:)` synchronously on `kAuthGlobalWorkQueue`, where `_currentUser` is read and written. Before, it ran on the caller's thread, which could race with the work queue. Using `sync` keeps the sign-out finished before the caller reports the error.
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces Auth.automaticSignOutNotification and associated keys to notify observers when a user is automatically signed out due to an invalid session or backend error. It updates the sign-out flow to use the new signOutAutomatically(withUserID:error:) method and adds comprehensive unit tests. The feedback suggests adding a dispatch precondition to signOutAutomatically to prevent potential deadlocks if called from kAuthGlobalWorkQueue.
|
Keeping it a draft PR until we determine if we want to do the API change. |
Summary
Add
Auth.automaticSignOutNotification, which is posted whenAuthsigns outthe current user because the backend reported that the user or the session is
no longer valid. Its
userInfohas the error that caused the sign-out and theID of the user who was signed out.
Fixes #16855. This adds public API, so it needs API review.
Why
Authsigns the user out when a request or token refresh fails withuserNotFound,userDisabled,invalidUserTokenoruserTokenExpired. Untilnow, apps only got
authStateDidChangeNotificationwith no current user, thesame as after
signOut(). The error reached the app only if the app's own callhad caused the sign-out. Refreshes started by other Firebase SDKs through
AuthInterop, or by the automatic token refresh, gave the app nothing. In#16498, users are signed out unexpectedly in production, and the app can't tell
an SDK sign-out from other causes.
Changes
Auth, next toauthStateDidChangeNotification:automaticSignOutNotification,automaticSignOutErrorKeyandautomaticSignOutUserIDKey.Auth.signOutAutomatically(withUserID:error:). OnkAuthGlobalWorkQueue, it signs out likesignOutByForce(withUserID:), thenposts the notification on the main queue.
User.signOutIfTokenIsInvalid(withError:)uses it. That covers both requesterrors and the token refresh path (
internalGetTokenAsync), includingrefreshes for
AuthInteropand the automatic token refresh.signOut()andUser.delete()are unchanged and don't post it.API proposal
Usage:
Notes for reviewers
authStateDidChangeNotification. TheAPI guidelines prefer
SendableAPIs withoutAny, so one alternative is anAsyncSequencelikeauthStateChanges(for exampleautomaticSignOuts)that yields a
Sendablestruct. Another option is an Objective-C constantlike
FIRAuthStateDidChangeNotification. This PR is Swift-only.state change notification for the sign-out, which
updateCurrentUserhasalready queued. A test checks this order.
signOutAutomaticallyruns synchronously onkAuthGlobalWorkQueue, likesignOut()(from review). So the sign-out stillfinishes before the failed call reports its error. None of its callers run on
that queue.
signOutByForce, now used only bydelete(), still runs on thecaller's thread, as before this PR.
byForce, the user is signed out even if removingthe stored user fails, so the notification is still posted.
invalidUserTokenanduserTokenExpiredfromrequests trigger a refresh instead. If the session has ended, the
notification has the refresh error.
Testing
UserTests:testAutomaticSignOutNotificationAfterRequestError:updatePasswordfailswith
USER_DISABLED. The notification is posted on the main thread, afterthe state change, with the error and the user ID, and the user is signed
out.
testAutomaticSignOutNotificationAfterTokenRefreshError: a forcedgetIDTokenResultfails withTOKEN_EXPIREDfrom the securetokenendpoint.
testSignOutDoesNotPostAutomaticSignOutNotificationandtestDeleteDoesNotPostAutomaticSignOutNotification.signOutByForcecalltemporarily restored, both positive tests fail. With
User.delete()temporarily routed through
signOutAutomatically, the delete test fails.xcodebuild -scheme AuthUnit -destination 'platform=iOS Simulator,name=iPhone 17 Pro,OS=26.5' IPHONEOS_DEPLOYMENT_TARGET=15.0 -test-timeouts-enabled YES -default-test-execution-time-allowance 60 test: 359 tests, 0 failures.new tests.
AuthAPNSTokenManagerTests.testGetTokenTimeoutWithMultipleWaitershung and was stopped. That's a race in
AuthAPNSTokenManager, which this PRdoesn't touch.
Related
Also found while triaging #16498: #16853 (listener's first call can be
nil)and #16854 (sign-out when a request's ID token is rejected).