Render App Bridge instead of login redirect when shop/host params are missing in embedded apps#3216
Open
byrichardpowell wants to merge 2 commits into
Open
Render App Bridge instead of login redirect when shop/host params are missing in embedded apps#3216byrichardpowell wants to merge 2 commits into
byrichardpowell wants to merge 2 commits into
Conversation
26c54ae to
a58ca7e
Compare
… missing in embedded apps When an embedded app receives a document request without shop or host query parameters (e.g. after a full page reload during Vite HMR), the server would redirect to the login page. This is incorrect because the app is still running inside the Shopify admin iframe — it just lost the URL search params during navigation/reload. Instead of redirecting to login, we now render a minimal App Bridge page. App Bridge detects it's in the admin iframe, retrieves the session token from the parent frame, and re-authenticates the app seamlessly. This fixes the annoying workflow during local development where: 1. Navigate to a new URL (loses the id_token param) 2. Make a code change 3. Vite triggers a full page reload (not HMR) 4. Server sees no identifiers and incorrectly shows login Non-embedded apps (ShopifyAdmin distribution) are unaffected and still redirect to login as before.
a58ca7e to
036e3f7
Compare
lizkenyon
reviewed
May 19, 2026
| shop, | ||
| }); | ||
| throw redirectToLoginPath(request, params); | ||
| throw renderAppBridgeOrError(request, params); |
Contributor
There was a problem hiding this comment.
Should we distinguish shop missing from invalid here?
For example I believe further down the call stack with this flow we will set the CSP headers with that invalid shop.
renderAppBridge() re-reads the raw query param and passes it into header construction
lizkenyon
approved these changes
May 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
During local development of embedded Shopify apps, there's an annoying workflow where the login screen appears inappropriately:
id_token/shop/hostquery params are lost from the URL)shoporhostparams and noAuthorizationheadervalidateShopAndHostParamsredirects to the login pageThis is incorrect because the app is still running inside the Shopify admin iframe — it just lost the URL search params during SPA navigation + reload.
Solution
In
validateShopAndHostParams, whenshoporhostparams are missing/invalid, we now render a minimal App Bridge page instead of redirecting to login.When App Bridge loads inside the Shopify admin iframe, it:
This is the same mechanism used by the existing "bounce page" flow (
/auth/session-token), just applied earlier in the auth pipeline.The previous code had a
throw redirect(config.auth.loginPath)fallback for non-embedded apps, butisEmbeddedAppis hardcoded totrueinshopify-app.tsfor all apps using this library (theShopifyAdmindistribution is already excluded earlier in the function). This made the login redirect dead code. This PR removes that dead code — no public APIs are changed.Note: this is a production behavior change, not just a dev improvement. Any request to an embedded app without
shop/hostparams (e.g., bookmarked deep links, direct URL access) will now get the App Bridge page instead of a login redirect. This is correct behavior — App Bridge will handle re-embedding in the admin.What changed
validate-shop-and-host-params.ts: Replaced theredirectToLoginPathhelper withrenderAppBridgeOrError. Instead of redirecting to the login page, renders the App Bridge page so it can recover the session. Removed the unusedredirectimport fromreact-routerand the dead login redirect code path. The login path guard (500 error whenauthenticate.admin()is called from the login route) is preserved.doc-request-path.test.ts: Updated the 4 test cases for missing/invalid shop/host params to expect an App Bridge page (200 with App Bridge script tag) instead of a login redirect (302).reject-bot-request.test.ts: Updated the "allowed Shopify agents" assertions to expect 200 (App Bridge page) instead of 302 (login redirect), confirming the request passes the bot check and proceeds to auth validation.expect-login-redirect.ts: Removed — no longer used by any test.__test-helpers/index.ts: Removed theexpect-login-redirectexport.