Skip to content

bug(settings): Abandoning sync sign flow can cause down stream problems#20098

Merged
dschom merged 1 commit intomainfrom
worktree-FXA-13160
Feb 25, 2026
Merged

bug(settings): Abandoning sync sign flow can cause down stream problems#20098
dschom merged 1 commit intomainfrom
worktree-FXA-13160

Conversation

@dschom
Copy link
Contributor

@dschom dschom commented Feb 24, 2026

Because

  • When a sync flow was abandoned at the 'confirm code' step, we would see problems on subsequent logins.

This pull request

  • Ensures the 'unverified session' errors are handled gracefully during oauth flow.
  • Ensures that we don't overwrite the local cache state with Firefox's user session state if it isn't actually verified. (This contributed to sign in loops and random failures).
  • Ensures that we actually set the sessionVerified state correctly on the account object cached in local storage. This had a similar effect as the previous issue.
  • Makes sure that discarding the session token also discards associated flags
  • Makes sure that discarding the session token is reactive.
  • Adds a little polish for setting the smart window to be enabled in nightly.

Issue that this pull request solves

Closes: FXA-13160

Checklist

Put an x in the boxes that apply

  • My commit is GPG signed.
  • If applicable, I have modified or added tests which pass locally.
  • I have added necessary documentation (if appropriate).
  • I have verified that my changes render correctly in RTL (if appropriate).

Screenshots (Optional)

Please attach the screenshots of the changes made in case of change in user interface.

Other information (Optional)

Any other information that is important to this pull request.

@dschom dschom force-pushed the worktree-FXA-13160 branch from 1d86eb7 to 35e3af4 Compare February 24, 2026 16:23
@dschom dschom force-pushed the worktree-FXA-13160 branch from 35e3af4 to ae22753 Compare February 24, 2026 16:57
@LZoog
Copy link
Contributor

LZoog commented Feb 24, 2026

@dschom I just tested it out and this works for the RP redirect case! \o/ But doesn't seem to fix it for the Smart Window case, I'll send a video in Slack.

@dschom dschom force-pushed the worktree-FXA-13160 branch from ae22753 to 685933c Compare February 25, 2026 02:33
@dschom dschom marked this pull request as ready for review February 25, 2026 02:35
@dschom dschom requested a review from a team as a code owner February 25, 2026 02:35
const account = currentAccount();
if (account) {
account.sessionToken = undefined;
account.verified = undefined;

Check warning

Code scanning / CodeQL

Prototype-polluting assignment Medium

This assignment may alter Object.prototype if a malicious '__proto__' string is injected from
user controlled input
.
if (account) {
account.sessionToken = undefined;
account.verified = undefined;
account.sessionVerified = undefined;

Check warning

Code scanning / CodeQL

Prototype-polluting assignment Medium

This assignment may alter Object.prototype if a malicious '__proto__' string is injected from
user controlled input
.
account.sessionToken = undefined;
account.verified = undefined;
account.sessionVerified = undefined;
account.hasPassword = undefined;

Check warning

Code scanning / CodeQL

Prototype-polluting assignment Medium

This assignment may alter Object.prototype if a malicious '__proto__' string is injected from
user controlled input
.
@dschom dschom force-pushed the worktree-FXA-13160 branch from 685933c to cf0ea01 Compare February 25, 2026 17:08

// Start sign-into-sync flow
await page.goto(
`${target.contentServerUrl}?context=fx_desktop_v3&service=sync&action=email&`
Copy link
Contributor

Choose a reason for hiding this comment

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

Just noting, this tests the Sync flow from over a year ago / non-oauth. I think we have multiples of these around so maybe we file a ticket to adjust our tests?

};
}

const nightlyProfile = isNightly
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice, in about a month we can have this on Release too (it's supposed to be true by default then as well) but it makes it easier in the meantime.

Copy link
Contributor Author

@dschom dschom Feb 25, 2026

Choose a reason for hiding this comment

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

Yeah, when testing I had to restart nightly alot. On successful sign in with smart window, smart window is then 'signed in', and I actually am not sure if there's a way to 'sign out' other than restarting.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think you currently have to switch to classic and then sign out from there. It seems to be ever-evolving hence my "I think" lol.

}

// Discard the current session token. We need to do this, otherwise, the cached session will be used
// and a new password will not be requested. This can lead to an infinite sign in loop.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: add the word entry or input like, "and a new password entry will not be requested" just to disambiguate between a fully new password?

const resp = await this.authClient.sessionStatus(token);
const isVerified = resp.state === 'verified';
setSessionVerified(isVerified);
return isVerified;
Copy link
Contributor

Choose a reason for hiding this comment

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

Aha, this totally makes sense given what we did at the GQL level with this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was bug was driving me crazy... I couldn't wrap my head around why the session would magically become verified 😅. I wouldn't doubt this will fix several other issues... since it was very common to get a false positive on session verified state.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah man, I just mentioned this in Slack, but I just realized now that session.isValid does the same thing that session.isSessionVerified does, so I think we probably want to leave this as-is and use session.isSessionVerified instead...

locationState.verificationMethod !== VerificationMethods.TOTP_2FA &&
error.errno === AuthUiErrors.UNVERIFIED_SESSION.errno
) {
return `/signin_token_code${navigationOptions.queryParams || ''}`;
Copy link
Contributor

Choose a reason for hiding this comment

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

This file just gets hairer and hairier 😅 (Does this send the email also?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, we should rethink how we do this... Using errors like this for flow control isn't ideal.

// don't assume it's valid.
if (
userFromBrowser?.sessionToken &&
userFromBrowser.verified === true
Copy link
Contributor Author

Choose a reason for hiding this comment

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

verified will be false when if the sync sign wasn't finalized. The sync menu will say 'finish setting up your account'. This intermediate state causes problems because the session is essentially unconfirmed, Overwriting the account state cached in local storage with a partially verified session, violated several down stream assumptions in the code, and gave the perception that the local storage cache wasn't responding to changes, when in fact it was.

await this.authClient.sessionStatus(token);
setSessionVerified(true);
return true;
const resp = await this.authClient.sessionStatus(token);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was a big problem. It was updating the cache such that the session was always. 'verified' when in fact it very well might not have been. When I finally fixed this, everything else fell into place.

);
if (error) {
if (
error.errno === AuthUiErrors.UNVERIFIED_SESSION.errno ||
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This state is definitely possible, and like the other two state indicates we need a confirmation code step. It happens when the session is in 'mustVerify' mode.

const resp = await this.authClient.sessionStatus(token);
const isVerified = resp.state === 'verified';
setSessionVerified(isVerified);
return isVerified;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was bug was driving me crazy... I couldn't wrap my head around why the session would magically become verified 😅. I wouldn't doubt this will fix several other issues... since it was very common to get a false positive on session verified state.

locationState.verificationMethod !== VerificationMethods.TOTP_2FA &&
error.errno === AuthUiErrors.UNVERIFIED_SESSION.errno
) {
return `/signin_token_code${navigationOptions.queryParams || ''}`;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, we should rethink how we do this... Using errors like this for flow control isn't ideal.

@dschom dschom force-pushed the worktree-FXA-13160 branch from cf0ea01 to e7b7864 Compare February 25, 2026 18:37
Copy link
Contributor

@LZoog LZoog left a comment

Choose a reason for hiding this comment

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

LGTM. Let's of course leave some notes for QA.

Sync unverified session RP redirect -> prompted for code
Sync unverified session Smart Window sign-in -> not shown cached sign-in, session token is discarded so we have the login restart and user is prompted for password
non-Sync non-2FA unverified session RP redirect -> works as expected going to the RP
non-Sync non-2FA unverified session Smart Window login -> works as expected allowing login

Poked around for other flows too. Note I didn't make sure that the signin token email is sent every time, but we already have some issues filed around signin emails.

With the change for, "only use the browser session token if it's fully verified", I think we should file a ticket for:

  • a bug I noticed for can_link_account: if you're in a partially verified state (sign in or sign up) and then click "Finish account set up" I'm taken to email-first. If I try to enter an email that is different than the one I'm partially signed in as, for an account that does NOT exist, we show the old merge warning dialog. The user shouldn't be able to continue in this state. Note I'm not sure off the top of my head what the fix is, because with the "can_link_account_uid capability, we send the UID up and for a new account it doesn't exist, but we don't want to show the old merge warning. Image
  • To help avoid the above scenario and to help the user sign in more easily, we can at least use the email from that isSignedInUser request, and prefill the email-first page
  • Just noting, in #20093, we removed the "Use another account" link if the user is currently signed into Firefox desktop. I think we want isSignedIntoFirefoxDesktop set, even if the session is not fully verified, because the entire point of removing it is to prevent users from hitting the "merge stop" (e.g. not being able to continue with the login, since if they start a login on a profile, they're going to have to use a different profile if they want to sign into a different account).

Thank you for the work on this ticket! \o/

…r login

Because:
- If a user only partially loged in from a FF sign in (sync, smartwindow, etc..), then subsquent logins would break.

This Commit:
- Configures nightly with smart window on
- Recreates issue with abadoning sync as functional test
- Fixes bogus cached account state
- Discard old session token in oauth recovery flow
- Handle unverified session scenarios during oauth flow
- Make sure functional test testing credentials include verified flag
@dschom dschom force-pushed the worktree-FXA-13160 branch from e7b7864 to 62a7ce2 Compare February 25, 2026 18:49
@dschom dschom merged commit 2b04bd0 into main Feb 25, 2026
22 checks passed
@dschom dschom deleted the worktree-FXA-13160 branch February 25, 2026 19:19
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.

2 participants