Skip to content

fix(auth): finish email-link reauthentication when the link opens after a cold launch - #1430

Open
demolaf wants to merge 2 commits into
auth/email-link-same-devicefrom
fix/email-link-reauth-cold-launch
Open

demolaf wants to merge 2 commits into
auth/email-link-same-devicefrom
fix/email-link-reauth-cold-launch

Conversation

@demolaf

@demolaf demolaf commented Nov 5, 2026 •

Copy link
Copy Markdown
Member

Finishes email-link reauthentication when the link opens the app after a cold launch, or while EmailLinkReauthView isn't showing. Previously the link was dropped. Stacked on #1429.

Changes

  • AuthPickerContent's onOpenURL completes a pending reauthentication through handleSignInLink(url:) when a user is signed in, and opens the sheet to show any error.
  • EmailLinkReauthView marks 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 whose onOpenURL fires more than once.
  • Integration test against the Auth emulator covering the cold launch, the reauth view showing, and the same link delivered twice.

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 a fuidemo:// 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

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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.

Suggested change
guard isReauthenticating, !isShowingEmailLinkReauth, currentUser != nil else { return }
guard isReauthenticating, !isShowingEmailLinkReauth, auth.currentUser != nil else { return }

Comment on lines 654 to 659
if isReauthenticating {
emailLinkInFlight = url
defer { emailLinkInFlight = nil }
try await handleEmailLinkReauthentication(url: url)
return
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

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.

Suggested change
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
}

@demolaf
demolaf marked this pull request as ready for review November 5, 2026 13:52
@demolaf
demolaf force-pushed the fix/email-link-reauth-cold-launch branch from 196e5b3 to 1e2afb8 Compare November 9, 2026 15:46
@demolaf
demolaf added this pull request to stack #1433 November 9, 2026 15:50
@demolaf
demolaf force-pushed the fix/email-link-reauth-cold-launch branch from 1e2afb8 to 71c1520 Compare November 10, 2026 14:30
…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.
@demolaf
demolaf force-pushed the fix/email-link-reauth-cold-launch branch from 71c1520 to 518defa Compare November 10, 2026 15:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant