Repository navigation
Conversation
There was a problem hiding this comment.
Code Review
This pull request adds support for completing email-link reauthentication when the app is opened via a deep link while the reauthentication view is not active, such as after a cold launch. The changes track the visibility of the reauthentication view, add a handler to process the link from the main entry point, and update integration tests. The review feedback identifies two critical issues: first, using the asynchronous currentUser property during a cold launch can cause the link to be silently dropped, so using the synchronous auth.currentUser is recommended; second, lastSignedInEmailLink should be updated upon successful reauthentication to prevent duplicate link deliveries from triggering unexpected errors.
| /// ``EmailLinkReauthView`` isn't showing, for example after a cold launch. Does nothing for | ||
| /// other links, or when that view is showing to handle the link itself. | ||
| internal func finishEmailLinkReauthentication(with url: URL) async throws { | ||
| guard isReauthenticating, !isShowingEmailLinkReauth, currentUser != nil else { return } |
There was a problem hiding this comment.
Using currentUser here can cause the reauthentication link to be silently dropped during a cold launch. AuthService.currentUser is populated asynchronously when the auth state listener fires, which happens on the next run loop cycle after initialization. If the app is opened via a deep link, onOpenURL can fire before this listener has run, meaning currentUser will be nil and the guard will fail. Using auth.currentUser (the underlying synchronous Firebase Auth property) avoids this asynchronous delay and ensures the user is correctly detected.
| guard isReauthenticating, !isShowingEmailLinkReauth, currentUser != nil else { return } | |
| guard isReauthenticating, !isShowingEmailLinkReauth, auth.currentUser != nil else { return } |
| if isReauthenticating { | ||
| emailLinkInFlight = url | ||
| defer { emailLinkInFlight = nil } | ||
| try await handleEmailLinkReauthentication(url: url) | ||
| return | ||
| } |
There was a problem hiding this comment.
If the reauthentication link is delivered again sequentially after reauthentication has completed (which is common on iOS due to multiple scene delegate or deep link callbacks), isReauthenticating will be false. Since lastSignedInEmailLink is not set during the reauthentication flow, the second delivery of the link will bypass the lastSignedInEmailLink check and proceed to the normal sign-in path. This will result in an unexpected error alert (e.g., "email address is missing from app storage") being shown to the user right after a successful reauthentication. Setting lastSignedInEmailLink = url upon successful reauthentication prevents this issue.
| if isReauthenticating { | |
| emailLinkInFlight = url | |
| defer { emailLinkInFlight = nil } | |
| try await handleEmailLinkReauthentication(url: url) | |
| return | |
| } | |
| if isReauthenticating { | |
| emailLinkInFlight = url | |
| defer { emailLinkInFlight = nil } | |
| try await handleEmailLinkReauthentication(url: url) | |
| lastSignedInEmailLink = url | |
| return | |
| } |
196e5b3 to
1e2afb8
Compare
1e2afb8 to
71c1520
Compare
…er a cold launch AuthPickerContent now completes reauthentication with a link the app opens while EmailLinkReauthView isn't showing, and leaves it to that view while it shows. A reauthentication link already in flight is ignored, so a link delivered to several onOpenURL handlers is used once.
…andled EmailLinkReauthView no longer resumes the waiting action when a second tap of the link returns while the first is still in flight. The email-link tests reset stored reauthentication state before each test, and the README notes the user retries the action after a cold-launch reauthentication.
71c1520 to
518defa
Compare
Finishes email-link reauthentication when the link opens the app after a cold launch, or while
EmailLinkReauthViewisn't showing. Previously the link was dropped. Stacked on #1429.Changes
AuthPickerContent'sonOpenURLcompletes a pending reauthentication throughhandleSignInLink(url:)when a user is signed in, and opens the sheet to show any error.EmailLinkReauthViewmarks itself as showing, so the top-level handler leaves the link to it and the link isn't handled twice.handleSignInLink(url:)gives a second call with a reauthentication link already in flight the first call's outcome, for content whoseonOpenURLfires more than once.Preview
Recorded in the e2e example app (
--test-view-enabled) on an erased simulator, with a local demo patch: the app keeps its session across relaunch, Delete Account requires a sign-in within the last 20 seconds (the Auth emulator never asks for a recent login), and afuidemo://scheme opens the emulator's links. Steps: signed in with an email link, Delete Account, Send Verification Email, kill and relaunch the app, open the reauthentication link, then Delete Account again.Before
Expected: when a user opens their reauthentication link after the app was closed, reauthentication should finish so Delete Account goes through.
before.mp4
After
after.mp4
Maintainer note: Fixes internal CPRN-652