Skip to content

feat(pairing): configurable iOS scheme and v2 iOS E2E coverage - #21125

Open
vbudhram wants to merge 1 commit into
mainfrom
fxa-pair-ios
Open

feat(pairing): configurable iOS scheme and v2 iOS E2E coverage#21125
vbudhram wants to merge 1 commit into
mainfrom
fxa-pair-ios

Conversation

@vbudhram

Copy link
Copy Markdown
Contributor

Because

  • The pairing hand-off hardcoded firefox://, which every Firefox iOS flavour registers, so it cannot reach a local or pre-release build when a release install is also present.
  • pair_oauth_start used the 500 ms default WebChannel deadline. A cold mobile start fetches the FxA client and OpenID configuration first, so it timed out and failed the whole pairing.
  • v2 pairing had no coverage against a real Firefox iOS supplicant.

This pull request

  • Adds a pairing.ios_url_scheme setting in configuration.js and beta-settings.js, surfaced as config.pairing.iosUrlScheme, and builds the iOS deep link from it in handoff.ts.
  • Extracts PAIR_OAUTH_TIMEOUT_MS in firefox.ts and exports it with the send default, so firefox.test.ts stops copying the numbers.
  • Adds pairingFlowV2iOS.spec.ts and ios-supplicant.ts: a Simulator supplicant against a Marionette authority, run twice — once with a test-built deep link, once with the link read off the rendered /pair page.
  • Restricts the v2 iOS and Android pairing specs to the local target.
  • Lets profile.mjs and the functional-test targets reach a stack off localhost.

Issue that this pull request solves

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

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).
  • I have manually reviewed all AI generated code.

Other information

Companion: needs the Firefox iOS side (FXIOS-16685) to answer pair_oauth_start.

Local tests:

  • npx jest src/lib/channels/firefox.test.ts (fxa-settings): 28 passed, 0 failed.
  • npx playwright test pairingFlowV2iOS.spec.ts --project=local: 2 passed, 0 failed.

@vbudhram
vbudhram requested a review from a team as a code owner August 31, 2026 20:32
Copilot AI balanced review requested due to automatic review settings August 31, 2026 20:32
* so a supplicant reached on another host needs this to agree or the sign-in page never
* receives its OAuth params.
*/
const LOCAL_HOST = process.env.FXA_LOCAL_HOST ?? 'localhost';

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is just some general cleanup so you can launch FxA pointing to a custom domain

channelId: string;
channelKey: string;
version: '1'|'2';
version: '1' | '2';

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Pretteri formatting

this.urlScheme = options.urlScheme || DEFAULT_URL_SCHEME;
}

/** Path to the prebuilt xctestrun, or undefined when the tests were never built. */

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Probably a better solution to this, but this is local only testing requring Firefox iOS simualtor make ok.

env: 'PAIRING_BROWSER_BUILD',
format: ['firefox', 'fenix'],
},
ios_url_scheme: {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We will need this to support opening different versions of Firefox iOS schemes

Copilot AI 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.

Pull request overview

Adds configurable Firefox iOS pairing handoffs and local-only v2 pairing coverage using a real iOS Simulator supplicant.

Changes:

  • Configures build-specific iOS URL schemes and longer pairing OAuth timeouts.
  • Adds iOS v2 end-to-end pairing infrastructure and documentation.
  • Supports functional testing against non-localhost stacks.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/fxa-settings/src/pages/Pair/Index/index.tsx Passes the configured iOS scheme into handoff planning.
packages/fxa-settings/src/lib/pairing/handoff.ts Builds configurable iOS deep links.
packages/fxa-settings/src/lib/pairing/handoff.test.ts Tests default and build-specific schemes.
packages/fxa-settings/src/lib/config.ts Defines the client-side pairing scheme setting.
packages/fxa-settings/src/lib/channels/firefox.ts Extends pairing OAuth WebChannel timeouts.
packages/fxa-settings/src/lib/channels/firefox.test.ts Tests the shared timeout constants.
packages/fxa-dev-launcher/profile.mjs Adds remote local-stack origins and pairing-version preferences.
packages/fxa-content-server/server/lib/configuration.js Defines the server-side iOS scheme configuration.
packages/fxa-content-server/server/lib/beta-settings.js Exposes the scheme to Settings.
packages/functional-tests/tests/pairing/pairingFlowV2iOS.spec.ts Adds v2 iOS pairing scenarios.
packages/functional-tests/tests/pairing/pairingFlowV2Android.spec.ts Restricts Android v2 coverage to local.
packages/functional-tests/tests/pairing/CLAUDE.md Documents mobile v2 test setup.
packages/functional-tests/lib/targets/local.ts Supports remote local-stack origins.
packages/functional-tests/lib/targets/firefoxUserPrefs.ts Configures Firefox for remote local origins.
packages/functional-tests/lib/pairing-helpers.ts Adds multi-route URL polling.
packages/functional-tests/lib/ios-supplicant.ts Implements Simulator and XCUITest orchestration.
Suppressed comments (1)

packages/functional-tests/tests/pairing/pairingFlowV2iOS.spec.ts:295

  • This fixed delay is still a race: the comment notes that delivering the URL before the scene exists loses it, but a loaded machine can take longer than 30 seconds to launch XCUITest. It also adds 30 seconds to every successful run. Have the iOS test signal that the app is ready, then open the URL in response to that signal instead of sleeping.
        const [deepLink] = await Promise.all([
          delivery.resolve({ ios, pairUrl, browser }),
          new Promise((r) => setTimeout(r, 30_000)),
        ]);

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

// unchanged. Override them to point at a stack reached on another host, such as a
// Tailscale address, which is what pairing with a real phone needs: the authority must
// mint pair URLs on the same origin the phone is configured against.
const LOCAL_HOST = process.env.FXA_LOCAL_HOST ?? 'localhost';
Comment on lines +74 to +76
...(target === 'local' && LOCAL_HOST !== 'localhost'
? { 'dom.securecontext.allowlist': LOCAL_HOST }
: {}),
Comment on lines +95 to +96
'find ~/Library/Developer/Xcode/DerivedData/Client-*/Build/Products ' +
'-maxdepth 1 -name "*SyncIntegration*xctestrun" 2>/dev/null | head -1',
Comment on lines +141 to +145
resolve: async ({ browser, pairUrl }: DeliveryContext) => {
const deepLink = await readHandoffDeepLink(browser, pairUrl);
expect(deepLink).toContain(encodeURIComponent(pairUrl));
return deepLink;
},
Because:
- Every Firefox iOS flavour registers `firefox://`, so a hardcoded scheme
  cannot reach a local build when a release install is also present.
- The v2 `pair_oauth_start` round trip does not fit the default WebChannel
  deadline on a cold start, and timing out there fails the whole pairing.
- v2 pairing against a real Firefox iOS supplicant had no coverage.

This commit:
- Adds a `pairing.ios_url_scheme` setting, surfaced as
  `config.pairing.iosUrlScheme`, and builds the iOS hand-off link from it.
- Names the pairing OAuth timeout `PAIR_OAUTH_TIMEOUT_MS` and exports it
  with the default, so the channel tests stop copying the numbers.
- Adds `pairingFlowV2iOS.spec.ts` and `lib/ios-supplicant.ts`, driving a
  Simulator supplicant against a Marionette authority.
- Restricts the v2 iOS and Android pairing specs to the local target.
- Lets the dev launcher and test targets reach a stack off localhost.

Co-Authored-By: Matt Lichtenstein <lichtensteinmp@gmail.com>
config.pairing.version === 2 &&
pairingVersion && pairingVersion === 2 &&
pairingChannelInfo && parseInt(pairingChannelInfo?.version) === 2
pairingVersion &&

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 don't get why these changed...

function buildIosDeepLink(target: string): string {
return `firefox://open-url?url=${encodeURIComponent(target)}`;
/**
* Opens `target` in the Firefox app. The scheme picks which build answers:

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.

Slop comment

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.

3 participants