Skip to content

Claim /oauth/native-callback as an iOS universal link - #100393

Merged
iwiznia merged 3 commits into
Expensify:mainfrom
software-mansion-labs:korytko/qa-auth-mobile-applinks
Sep 8, 2026
Merged

iwiznia merged 3 commits into
Expensify:mainfrom
software-mansion-labs:korytko/qa-auth-mobile-applinks

Conversation

@JakubKorytko

@JakubKorytko JakubKorytko commented Sep 4, 2026 •

Copy link
Copy Markdown
Member

Explanation of Change

Claims /oauth/native-callback as an iOS Universal Link so the Cloudflare Access OAuth callback of the native
sign-in flow (ASWebAuthenticationSession with an https callback, iOS 17.4+) returns to the app. One
components entry added to .well-known/apple-app-site-association, next to the existing Plaid OAuth entry.
appIDs are untouched.

The path is native-only on purpose. Web keeps /oauth/callback, which no app claims, so a sign-in started in
Safari stays in Safari where its PKCE state lives.

Because the standalone NewDot entitlements in this repo already list applinks:new.expensify.com and
applinks:staging.new.expensify.com, this claim is live on production iOS as soon as the file deploys, without
an app release. Since no screen exists at the path, linkingConfig.filter drops it for both cold start and
warm url events so a tap never lands on NotFound (isNativeOAuthCallbackURL, unit tested).

Android is not covered here: assetlinks.json handle_all_urls is only the verification relation, the
intent-filter in Mobile-Expensify decides what opens the app and is a separate PR, as is the HybridApp
entitlement.

Fixed Issues

$ #91419
PROPOSAL: N/A

Tests

This is a static claim file with no runtime behaviour in the app, so there is nothing to click. It is only
verifiable once deployed, and only together with the Mobile-Expensify entitlement PR.

  1. After this is deployed, run curl -s https://new.expensify.com/apple-app-site-association | jq '.applinks.details[0].components[] | select(."/" == "/oauth/native-callback")'
  2. Verify that the entry is returned, and that the response Content-Type is application/json
  3. Verify that existing Universal Links still work: on an iOS device with the app installed, open Notes, paste
    https://new.expensify.com/settings and tap it (the Safari address bar does not trigger Universal Links)
  4. Verify that the app opens on the Settings screen rather than Safari, confirming the file is still valid and
    no existing claim regressed

Offline tests

N/A

QA Steps

  1. On an iOS device with the staging app installed, open Notes and paste https://staging.new.expensify.com/settings
  2. Tap the link
  3. Verify that the app opens on the Settings screen instead of Safari, confirming existing Universal Link
    handling is unaffected

PR Author Checklist

  • I linked the correct issue in the ### Fixed Issues section above
  • I wrote clear testing steps that cover the changes made in this PR
    • I added steps for local testing in the Tests section
    • I added steps for the expected offline behavior in the Offline steps section
    • I added steps for Staging and/or Production testing in the QA steps section
    • I added steps to cover failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
    • I tested this PR with a High Traffic account against the staging or production API to ensure there are no regressions (e.g. long loading states that impact usability).
  • I included screenshots or videos for tests on all platforms
  • I ran the tests on all platforms & verified they passed on:
    • Android: Native
    • Android: mWeb Chrome
    • iOS: Native
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • I verified there are no console errors (if there's a console error not related to the PR, report it or open an issue for it to be fixed)
  • I followed proper code patterns (see Reviewing the code)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I followed the guidelines as stated in the Review Guidelines
  • I tested other components that can be impacted by my changes (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar are working as expected)
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))
  • If new assets were added or existing ones were modified, I verified that:
    • The assets are optimized and compressed (for SVG files, run npm run compress-svg)
    • The assets load correctly across all supported platforms.
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • I added unit tests for any new feature or bug fix in this PR to help automatically prevent regressions in this user flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.

Screenshots/Videos

Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari

@JakubKorytko
JakubKorytko force-pushed the korytko/qa-auth-mobile-applinks branch from a6584c6 to 851a882 Compare September 4, 2026 11:45
@JakubKorytko

Copy link
Copy Markdown
Member Author

@codex review

@JakubKorytko

Copy link
Copy Markdown
Member Author

@MelvinBot review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 851a882984

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread .well-known/apple-app-site-association Outdated
@MelvinBot

MelvinBot commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor

🤖 Review

The JSON is valid and the domain choice is right, but the PR description is wrong on two counts and the claim ships a small regression before anything can use it.

1. This PR alone activates the claim on production iOS — the Mobile-Expensify PR doesn't gate it.

The description says the matching Associated Domains entitlement "lives in Mobile-Expensify and is a separate PR." That's only true for com.expensify.expensifylite (HybridApp). The standalone NewDot app already ships the entitlement in this repo — all 8 files carry applinks:new.expensify.com and applinks:staging.new.expensify.com, and associated-domains entitlements aren't path-scoped. Apple's CDN refetches the AASA periodically, so this reaches already-installed apps with no app release.

2. Nothing on iOS consumes /oauth/callback, and there's no route — so the only effect today is a Not Found screen.

QA auth is hard-off on native: Config/index.native.ts:8 returns false unconditionally, and finishSignInFromURL/index.native.ts:4 is a no-op. There's no route either, so the path falls through to [SCREENS.NOT_FOUND]: '*'.

So a real tap on https://new.expensify.com/oauth/callback from Mail/Slack/Notes goes from "opens Safari, web boot hook rewrites the URL to somewhere real" to "opens the app on Not Found." Low frequency, but no upside until the native side lands.

There's a direct precedent: /partners/plaid/oauth_ios is the only other routeless path claimed in this file, and it needed a dedicated bail-out at subscribe.ts:51-60 whose comment says forwarding it "would resolve to NotFound." Suggest either landing this alongside the native handler, or adding the equivalent guard now (note subscribe.ts only covers warm start — cold start goes through getAdaptedStateFromPath).

3. The Android claim in the description is incorrect.

handle_all_urls in assetlinks.json is the Digital Asset Links verification relation — it says the site delegates verification to the app. What decides which URLs actually open the app is the <intent-filter> <data> list in AndroidManifest.xml, which is 44 pathPrefix entries with zero mention of /oauth. /oauth/callback will not open the Android app. Not a blocker — nothing would handle it anyway — but the description shouldn't say the path is "already covered."

4. Question: will the callback arrive as a Universal Link at all?

The flow ends with Cloudflare issuing a server-side 302 back to redirect_uri after window.location.assign(authorizeURL). iOS doesn't fire Universal Links for server-side redirects, and an in-app SFSafariViewController never hands one back to its presenting app. Every existing native OAuth flow in App uses expo-web-browser's openAuthSessionAsync with a custom scheme — new-expensify://, see ConnectToHRFlow/index.ios.tsx:49 and SAMLSignInPage/index.native.tsx:105 — which needs no AASA claim. ASWebAuthenticationSession.Callback.https(host:path:) (iOS 17.4+) does use the associated domain, so if that's the mechanism in the Mobile-Expensify PR then this entry is correct. Could you confirm which one it is?

What checks out
  • Domain is right. The redirect URI is built from window.location.origin, i.e. the NewDot origin, not the QA API host (getQAOrigin). So claiming the path on new.expensify.com / staging.new.expensify.com is the correct target, and those are exactly the two buckets deploy.yml publishes this file to (with Content-Type: application/json forced).
  • JSON is valid, 27 components, no duplicate paths.
  • Ordering is safe. No earlier pattern in the array matches /oauth/callback, so position between the Plaid and statements entries doesn't change matching.
  • Exact path, no wildcard, and query params are ignored by "/" matching, so ?code=…&state=… still matches. Format and the comment key are consistent with the rest of the file.
  • One nit: the comment says "for the QA environment," but this file is served on production and staging and there is no per-environment variant. Worth rewording so nobody later reads it as QA-scoped.
Why I didn't run an app test

This is a static claim file with no runtime behaviour in the web or Android app, and the iOS effect can only be observed on a physical device against a deployed AASA. Nothing here is verifiable in the automated test environment, so I reviewed statically instead.


Reply with @MelvinBot <feedback> to refine this review.


view run · no recording available

@JakubKorytko

Copy link
Copy Markdown
Member Author

@MelvinBot re-review please, all four raised points addressed

@JakubKorytko

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

Reviewed commit: 5e45480bf5

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@MelvinBot

MelvinBot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

🤖 Re-review

Points 1, 3 and 4 are addressed. Point 2 is half-fixed: filter covers React Navigation, but App has a second deep-link consumer that never goes through linkingConfig, so the NotFound landing is still reachable — for a signed-out user, which is the state a sign-in flow is in.

The gap: DeepLinkHandler → openReportFromDeepLink

The filter itself is right. In the installed @react-navigation/native@7.1.33, useLinking.native.js applies it inside getStateFromURL, which both getInitialState (cold start) and the url listener call — so the "covers both" comment holds, and it's a better choice than a subscribe.ts guard for exactly that reason.

But DeepLinkHandler reads the URL itself, outside React Navigation's linking — src/DeepLinkHandler.tsx:75 (cold) and src/DeepLinkHandler.tsx:137 (warm) — and passes it to openReportFromDeepLink, which still navigates:

  • Link.ts:510 — /oauth/native-callback is not a public screen route, so it isn't dropped here
  • Link.ts:606-608 — the "navigation for signed users is handled by react-navigation" early return only fires when hasAuthToken() was true at capture time
  • Link.ts:629 — so once waitForUserSignIn() resolves it runs Navigation.navigate('/oauth/native-callback?code=…') → NotFound

Signed in, this is fine (it bails at 607 and the filter handles it). Signed out, the user gets dumped on NotFound right after signing in.

This is the same shape as the Plaid precedent I flagged last time: that path needs its bail-out in two places, subscribe.ts:51-54 and Link.ts:519-522. Suggest adding the equivalent guard next to the Plaid one in openReportFromDeepLink — it already has the full url, so isNativeOAuthCallbackURL(url) drops in directly.

Two minor notes
  • filter is inert on web. The dom useLinking.js in this version never reads filter (only useLinking.native.js does), so the shared linkingConfig change is native-only in effect. Harmless today since web keeps /oauth/callback, but the comment in linkingConfig/index.ts reads as cross-platform.
  • Last-visited-path restore is suppressed by a filtered link. A claimed URL still counts as a "specific deep link" at NavigationRoot.tsx:179-183, so a signed-in user who taps the callback link externally boots to the default screen instead of their last path. Cosmetic, and it goes away if the link is never externally tappable.
  • Optional consistency nit: the web redirect URI is still a literal at CloudflareAccess/Config/index.ts:54 while the native path now has a CONST.
What checks out
  • All three earlier description errors are fixed. The PR body now states the claim goes live on production iOS from this file alone, explains handle_all_urls is only the Digital Asset Links verification relation with the intent-filter deciding what opens the Android app, and answers point 4 — ASWebAuthenticationSession with an https callback (iOS 17.4+) does use the associated domain, so the AASA entry is the right mechanism.
  • The path split is the right call. A native-only /oauth/native-callback keeps a Safari-started sign-in in Safari where its PKCE state lives, and nothing in App generates it yet — web still builds /oauth/callback and native isQAAuthConfigured() is still hard false.
  • AASA is valid. 27 components, no duplicate paths, no earlier pattern matches /oauth/native-callback, exact path so ?code=…&state=… still matches, format and comment key consistent with the file.
  • isNativeOAuthCallbackURL is sound. Exact pathname comparison, try/catch for non-URL input, and pathname is available on native either way (react-native-url-polyfill/auto is loaded via @libs/Url, and RN's own URL implements pathname). The four unit tests cover the host+query, web-path, substring, and non-URL cases.
  • CI is green apart from Check independent approval.
Why there's no app test

I attempted a web smoke test to confirm the shared linkingConfig change didn't regress web routing. The prestarted web session in this run never rendered past the post-sign-in "Welcome!" screen — blank viewport, zero DOM nodes and no network activity across ~90s, with a clean dev-server build — so no verification could be exercised. That's a harness/session problem, not a signal about this diff.

It also wouldn't have observed anything: filter is not read by React Navigation's web useLinking, and the iOS effect is only visible on a physical device against a deployed AASA. So I reviewed statically instead.


Reply with @MelvinBot <feedback> to refine this review.


view run · view recording

@JakubKorytko

Copy link
Copy Markdown
Member Author

@MelvinBot yeah, valid, added the same guard next to the Plaid one, and reworded the filter comment to say native only. Please re-review

@codecov

codecov Bot commented Sep 7, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Looks like you've decreased code coverage for some files. Please write tests to increase, or at least maintain, the existing level of code coverage. See our documentation here for how to interpret this table.

Files with missing lines Coverage Δ
src/CONST/index.ts 91.22% <ø> (ø)
src/libs/Navigation/linkingConfig/index.ts 100.00% <100.00%> (ø)
...vigation/linkingConfig/isNativeOAuthCallbackURL.ts 100.00% <100.00%> (ø)
src/libs/actions/Link.ts 51.28% <50.00%> (-0.01%) ⬇️
... and 184 files with indirect coverage changes

@MelvinBot

MelvinBot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

🤖 Re-review

Both changes are right — the gap is closed and I have nothing blocking left.

The guard at Link.ts:525-529 sits next to the Plaid one at Link.ts:520-523, before waitForUserSignIn(), and passes url rather than route — which is the part that matters, since isNativeOAuthCallbackURL needs an absolute URL for new URL() and would silently return false on a bare path. That closes both DeepLinkHandler entries: cold start (DeepLinkHandler.tsx:75) and the warm url listener (DeepLinkHandler.tsx:137), both of which route through openReportFromDeepLink. It also lands after doneCheckingPublicRoom(), so the splash still unblocks. The reworded linkingConfig/index.ts:18-22 comment now matches the code — filter really is only read by useLinking.native, and the cross-reference to the openReportFromDeepLink guard is the useful part.

The only other URL consumer I found is Report/index.ts:565, which just extracts a reportID and never navigates, so it needs nothing.

One optional leftover, unchanged from last round and purely cosmetic: a filtered callback URL still counts as a "specific deep link" at NavigationRoot.tsx:179-183, so a signed-in user who taps the link externally boots to the default screen instead of their last visited path. Fine to leave.

CI is green — 27 checks passing, perf tests still running, and Check independent approval is the only failure.

🤖 Web test steps verification results: ✅ pass

All three regression checks pass on dev NewDot web: Home/inbox render correctly, the Concierge chat/report loads, and account menu navigation to Profile works. No console or network errors observed.

Profile settings screen

Step Status
App loads to Home and the inbox renders (no blank screen, no Not Found)Snapshot showed heading "Home" with full content; switching to Inbox rendered the LHN chat list and report pane. All API requests returned 200.
Home screen rendered with greeting and quick actions
Inbox LHN with Expensify Concierge chat and report pane
✅
Opening a chat from the inbox still works and the report loadsClicking the Concierge entry navigated to the report route and the transcript rendered with a working compose box.
Inbox LHN with Expensify Concierge chat and report pane
✅
Navigating in-app to Settings (account menu → Profile) opens the Profile screenNavigated to /settings/profile and rendered heading "Profile" with the Public and Private sections.
Profile settings screen
✅

This exercises the shared code the diff touches — openReportFromDeepLink runs on web too. The iOS-specific effect still can't be verified here; it needs a device against a deployed AASA.


Reply with @MelvinBot <feedback> to refine this review.


view run · view recording

@JakubKorytko
JakubKorytko marked this pull request as ready for review September 7, 2026 14:21
@JakubKorytko
JakubKorytko requested review from a team as code owners September 7, 2026 14:21
@melvin-bot
melvin-bot Bot requested review from dylanexpensify and huult and removed request for a team September 7, 2026 14:21
@melvin-bot

melvin-bot Bot commented Sep 7, 2026

Copy link
Copy Markdown

@huult Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button]

@JakubKorytko JakubKorytko changed the title Claim /oauth/callback as an iOS universal link Claim /oauth/native-callback as an iOS universal link Sep 7, 2026
@huult

huult commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

I will take a look this pr soon

@dylanexpensify dylanexpensify 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.

No product review required

@huult

huult commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

Reviewer Checklist

  • I have verified the author checklist is complete (all boxes are checked off).
  • I verified the correct issue is linked in the ### Fixed Issues section above
  • I verified testing steps are clear and they cover the changes made in this PR
    • I verified the steps for local testing are in the Tests section
    • I verified the steps for Staging and/or Production testing are in the QA steps section
    • I verified the steps cover any possible failure scenarios (i.e. verify an input displays the correct error message if the entered data is not correct)
    • I turned off my network connection and tested it while offline to ensure it matches the expected behavior (i.e. verify the default avatar icon is displayed if app is offline)
  • I checked that screenshots or videos are included for tests on all platforms
  • I included screenshots or videos for tests on all platforms
  • I verified that the composer does not automatically focus or open the keyboard on mobile unless explicitly intended. This includes checking that returning the app from the background does not unexpectedly open the keyboard.
  • I verified tests pass on all platforms & I tested again on:
    • Android: HybridApp
    • Android: mWeb Chrome
    • iOS: HybridApp
    • iOS: mWeb Safari
    • MacOS: Chrome / Safari
  • If there are any errors in the console that are unrelated to this PR, I either fixed them (preferred) or linked to where I reported them in Slack
  • I verified proper code patterns were followed (see Reviewing the code)
    • I verified that comments were added to code that is not self explanatory
    • I verified that any new or modified comments were clear, correct English, and explained "why" the code was doing something instead of only explaining "what" the code was doing.
    • I verified any copy / text that was added to the app is grammatically correct in English. It adheres to proper capitalization guidelines (note: only the first word of header/labels should be capitalized), and is either coming verbatim from figma or has been approved by marketing (in order to get marketing approval, ask the Bug Zero team member to add the Waiting for copy label to the issue)
  • If a new code pattern is added I verified it was agreed to be used by multiple Expensify engineers
  • I verified that this PR follows the guidelines as stated in the Review Guidelines
  • I verified other components that can be impacted by these changes have been tested, and I retested again (i.e. if the PR modifies a shared library or component like Avatar, I verified the components using Avatar have been tested & I retested again)
  • If a new component is created I verified that:
    • A similar component doesn't exist in the codebase
    • All props are defined accurately
    • The component has a clear name that is non-ambiguous and the purpose of the component can be inferred from the name alone
    • The only data being stored in the state is data necessary for rendering and nothing else
    • The component has the minimum amount of code necessary for its purpose, and it is broken down into smaller components in order to separate concerns and functions
  • If a new CSS style is added I verified that:
    • A similar style doesn't already exist
    • The style can't be created with an existing StyleUtils function (i.e. StyleUtils.getBackgroundAndBorderStyle(theme.componentBG)
  • If the PR modifies code that runs when editing or sending messages, I tested and verified there is no unexpected behavior for all supported markdown - URLs, single line code, code blocks, quotes, headings, bold, strikethrough, and italic.
  • If the PR modifies a generic component, I tested and verified that those changes do not break usages of that component in the rest of the App (i.e. if a shared library or component like Avatar is modified, I verified that Avatar is working as expected in all cases)
  • If the PR modifies a component related to any of the existing Storybook stories, I tested and verified all stories for that component are still working as expected.
  • If the PR modifies a component or page that can be accessed by a direct deeplink, I verified that the code functions as expected when the deeplink is used - from a logged in and logged out account.
  • If the PR modifies the UI (e.g. new buttons, new UI components, changing the padding/spacing/sizing, moving components, etc) or modifies the form input styles:
    • I verified that all the inputs inside a form are aligned with each other.
    • I added Design label and/or tagged @Expensify/design so the design team can review the changes.
  • For any bug fix or new feature in this PR, I verified that sufficient unit tests are included to prevent regressions in this flow.
  • If the main branch was merged into this PR after a review, I tested again and verified the outcome was still expected according to the Test steps.
  • I have checked off every checkbox in the PR reviewer checklist, including those that don't apply to this PR.

Screenshots/Videos

Android: HybridApp
Android: mWeb Chrome
iOS: HybridApp
iOS: mWeb Safari
MacOS: Chrome / Safari

@huult huult 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.

The code changes LGTM. We can't verify this until it is deployed, so I'll verify it once it's deployed.

@melvin-bot
melvin-bot Bot requested a review from iwiznia September 8, 2026 07:57
@iwiznia
iwiznia merged commit caa6e8c into Expensify:main Sep 8, 2026
39 of 43 checks passed
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

🚧 iwiznia has triggered a test Expensify/App build. You can view the workflow run here.

@OSBotify

OSBotify commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release.

@OSBotify OSBotify mentioned this pull request Sep 8, 2026
69 tasks done
@OSBotify

OSBotify commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

🚀 Deployed to staging by https://github.com/iwiznia in version: 9.4.74-0 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

@OSBotify

OSBotify commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

🚀 Deployed to production by https://github.com/mountiny in version: 9.4.74-2 🚀

platform result
🕸 web 🕸 success ✅
🤖 android 🤖 success ✅
🍎 iOS 🍎 success ✅

@huult

huult commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

After this is deployed, run curl -s https://new.expensify.com/apple-app-site-association | jq '.applinks.details[0].components[] | select(."/" == "/oauth/native-callback")'
Verify that the entry is returned, and that the response Content-Type is application/json

Screenshot 2026-09-11 at 20 47 48

Verify that existing Universal Links still work: on an iOS device with the app installed, open Notes, paste
https://new.expensify.com/settings and tap it (the Safari address bar does not trigger Universal Links)
Verify that the app opens on the Settings screen rather than Safari, confirming the file is still valid and
no existing claim regressed

ScreenRecording_09-11-2026.20-48-24_1.MP4

The code changes LGTM. We can't verify this until it is deployed, so I'll verify it once it's deployed.

@iwiznia @JakubKorytko This PR has been deployed to production, and I verified that it’s working as expected.

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.

6 participants