feat(pairing): configurable iOS scheme and v2 iOS E2E coverage - #21125
feat(pairing): configurable iOS scheme and v2 iOS E2E coverage#21125vbudhram wants to merge 1 commit into
Conversation
| * 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'; |
There was a problem hiding this comment.
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'; |
There was a problem hiding this comment.
Pretteri formatting
| this.urlScheme = options.urlScheme || DEFAULT_URL_SCHEME; | ||
| } | ||
|
|
||
| /** Path to the prebuilt xctestrun, or undefined when the tests were never built. */ |
There was a problem hiding this comment.
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: { |
There was a problem hiding this comment.
We will need this to support opening different versions of Firefox iOS schemes
There was a problem hiding this comment.
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'; |
| ...(target === 'local' && LOCAL_HOST !== 'localhost' | ||
| ? { 'dom.securecontext.allowlist': LOCAL_HOST } | ||
| : {}), |
| 'find ~/Library/Developer/Xcode/DerivedData/Client-*/Build/Products ' + | ||
| '-maxdepth 1 -name "*SyncIntegration*xctestrun" 2>/dev/null | head -1', |
| 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>
defddce to
dfbcd6d
Compare
| config.pairing.version === 2 && | ||
| pairingVersion && pairingVersion === 2 && | ||
| pairingChannelInfo && parseInt(pairingChannelInfo?.version) === 2 | ||
| pairingVersion && |
There was a problem hiding this comment.
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: |
Because
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_startused 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.This pull request
pairing.ios_url_schemesetting inconfiguration.jsandbeta-settings.js, surfaced asconfig.pairing.iosUrlScheme, and builds the iOS deep link from it inhandoff.ts.PAIR_OAUTH_TIMEOUT_MSinfirefox.tsand exports it with the send default, sofirefox.test.tsstops copying the numbers.pairingFlowV2iOS.spec.tsandios-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/pairpage.localtarget.profile.mjsand 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
xin the boxes that applyOther 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.