Skip to content

Support new Firefox webchannel message to resume login#19893

Merged
vbudhram merged 1 commit intomainfrom
fxa-12118
Jan 28, 2026
Merged

Support new Firefox webchannel message to resume login#19893
vbudhram merged 1 commit intomainfrom
fxa-12118

Conversation

@vbudhram
Copy link
Copy Markdown
Contributor

@vbudhram vbudhram commented Jan 15, 2026

Because

  • Users refreshing the confirmation code page during Firefox Sync OAuth flow saw "Bad Request" error
  • In-memory OAuth keys (keyFetchToken, unwrapBKey) are lost on page refresh with no recovery path

This pull request

  • Adds useOAuthFlowRecovery hook to recover from interrupted OAuth Native flows via Firefox webchannel
  • Adds fxaOAuthFlowBegin and fxaOAuthFlowIsActive commands to firefox.ts
  • Updates SigninTokenCode and ConfirmSignupCode to attempt recovery when state is missing
  • Redirects to /signin with fresh OAuth params on successful recovery

Issue

Closes: https://mozilla-hub.atlassian.net/browse/FXA-12118

Checklist

  • My commit is GPG signed
  • Tests pass locally (if applicable)
  • Documentation updated (if applicable)
  • RTL rendering verified (if UI changed)

Other Information

How to test:

  1. Open Firefox → Settings → Sync → Sign in
  2. Enter credentials, reach confirmation code page
  3. Refresh the page (F5)
  4. Should redirect to /signin instead of "Bad Request" error

Note: Requires Firefox with fxaccounts:oauth_flow_begin webchannel support

YOu can download it here: https://drive.google.com/file/d/1oKZHHJXqJ5C0LniU-iJwxnLoCQE1FvWt/view?usp=drive_link

@vbudhram vbudhram self-assigned this Jan 15, 2026
@vbudhram vbudhram marked this pull request as ready for review January 26, 2026 19:19
@vbudhram vbudhram requested a review from a team as a code owner January 26, 2026 19:19
@vbudhram vbudhram changed the title Fxa 12118 Support new Firefox webchannel message to resume login Jan 26, 2026
Copy link
Copy Markdown
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.

I have latest Nightly, but I've tried 3 times and keep getting this error on confirm signup refresh:

Image

useEffect(() => {
if (recoveryFailed) {
const localizedErrorMessage = ftlMsg.getMsg(
'signin-recovery-error',
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.

Looks like we need this added to an FTL file.

import firefox from '../../channels/firefox';
import { hardNavigate } from 'fxa-react/lib/utils';

export type OAuthFlowRecoveryResult = {
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.

Just a nit but it could be good to have JS Doc style comments for this type, mostly because I'm not sure if I'd know what isRecovering means in this context without digging in a bit more. Something like, "The oauth query params from the browser are no longer valid (oftentimes from a page refresh) - are we currently fetching fresh params via webchannel?" would make it super clear to me.

stack: string;
};
};
params?: Record<string, any>; // Some commands use params instead of data
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.

Since we later check const responseData = message.data || message.params I guess we're saying one or the other is present - we could do something like...

interface FirefoxMessageBase {
  command: FirefoxCommand;
  messageId: string;
  error?: string;
}

interface FirefoxMessageWithData extends FirefoxMessageBase {
  data: Record<string, any> & {
    error?: {
      message: string;
      stack: string;
    };
  };
  params?: never;
}

interface FirefoxMessageWithParams extends FirefoxMessageBase {
  params: Record<string, any>;
  data?: never;
}

export type FirefoxMessage =
  | FirefoxMessageWithData
  | FirefoxMessageWithParams;

... Feels like a lot of types but could be worth it for clarity? I'll leave it up to you.

getPermissions: jest
.fn()
.mockReturnValue(['profile', 'https://identity.mozilla.com/apps/oldsync']),
};
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.

I guess one small improvement here is setting the return type to as unknown as Integration so you don't have to set each one as any further down in each test. Better though would be no casting here at all - it'd require setting integration: Integration in the hook, to something like integration: UseOAuthFlowRecoveryIntegration and then setting that to only what we need, similar to how we do others like SigninIntegration. 🤔

useEffect(() => {
const shouldAttemptRecovery =
!recoveryAttempted &&
isOAuthNativeIntegration(integration) &&
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.

Makes sense to check this for isOAuthNativeIntegration and not just Sync flow 👍

There might be an opportunity to share more of this in the hook so any time we use it it'd prevent some extra set up but eh maybe this is more clear and we don't need it in too many places.

@LZoog LZoog self-assigned this Jan 27, 2026
Copy link
Copy Markdown
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.

I'm having problems getting Firefox running from the download link and this isn't available in Nightly yet so I'm going to r+ based on what I see in the code, and sounds like we might pair for a bit later too.

My only real "blocker" is the FTL file update, but had one or two other suggestions as well.

@vbudhram vbudhram requested a review from a team as a code owner January 28, 2026 17:51
@vbudhram vbudhram merged commit e30a7d7 into main Jan 28, 2026
21 checks passed
@vbudhram vbudhram deleted the fxa-12118 branch January 28, 2026 18:23
new CustomEvent(FirefoxCommand.Error, { detail: error })
);
} else {
const responseData = message.data || message.params;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Sammy pointed out that desktop really should have used data in the first place, and pointed this line out to show how now either works. So I took the liberty of changing it to data instead of params, meaning this work-around can be removed if you like. cc @LZoog @vbudhram @skhamis

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.

4 participants