Our embedded-app-check “Using session tokens for user authentication” has been stuck red across 6+ two-hour cycles (12+ hours), blocking Submit for review. The sibling “Using the latest App Bridge script from CDN” check is green, and every other preliminary step is green. Only this one check is red.
App ID: 389108826113 · Client ID: 2365b3548aeaf55c65f33a5a0a0512d5 · Partner: 5008684 · App URL: https://vestfold-hall.onrender.com
WHAT WE HAVE VERIFIED IS CORRECT
- Stack is the unmodified official template: @shopify/shopify-app-react-router v1.1.0 (token-exchange default) + @shopify/app-bridge-react v4. AppProvider (embedded, apiKey) renders the App Bridge CDN script tag with data-api-key server-side, so the API key is present in the initial server-rendered HTML (not injected late). Every loader calls authenticate.admin().
- SHOPIFY_API_KEY exactly equals the client ID above.
- Live request behavior (verified with a real browser User-Agent): an unauthenticated embedded request to /app?..&embedded=1 returns 302 to /auth/session-token?..&shopify-reload=…, and /auth/session-token returns 200 serving the tag: script data-api-key=“2365b3548a…” src=“https://cdn.shopify.com/shopifycloud/app-bridge.js”. This is pure token exchange with no OAuth redirect.
- Availability: the app is always-on. First hit after idle returns HTTP 200 in ~0.3-0.5s. No 502, no cold-start.
- CSP frame-ancestors on our app pages includes Shopify’s own test infrastructure: https://*.spin.dev, https://admin.myshopify.io, https://admin.shop.dev, plus the merchant admin. So the automated checker is able to frame the app.
- Admin GraphQL requests succeed in our Dev Dashboard logs, confirming the session-token → token-exchange → Admin API chain works end-to-end.
EVERYTHING WE HAVE TRIED (still red after all of it)
- Confirmed the app is installed and loads cleanly, embedded, inside the Shopify admin on our designated dev store (vh-agent-dev-store.myshopify.com) — the store tied to the app in the CLI project config.
- Found and cleared a scope-grant wall: a recently added scope (read_legal_policies) had frozen the app on the “needs access / Update” screen on that store, so it was doing no clean session-token auth. We granted the scope and confirmed the app then loads clean and does token exchange.
- Performed a full clean uninstall and reinstall of the app on vh-agent-dev-store.
- Opened the app embedded in a normal browser with ALL extensions disabled (fresh/incognito profile, no ad-blockers or privacy extensions that could interfere with App Bridge’s session-token fetch), and clicked through several pages (Home, Reports, Settings, Conversations, Plans, Support) multiple times so App Bridge fetched a session token repeatedly.
- Waited across 6+ full check cycles (the check auto-runs about every 2 hours). We did NOT redeploy or change anything during this window, so the app was stable and responding the whole time. 12+ hours later the check is still red.
We have exhausted everything observable from our side — the code, the live auth behavior, availability, framing/CSP, a clean reinstall, and an extensions-off interactive session across many check cycles. The implementation is a standard, correct App Bridge v4 + session-token/token-exchange setup, and it demonstrably works.
Please manually verify / clear this check, or tell us exactly what the automated monitor needs to observe that our verification above does not already show. Full logs are available on request. Thank you.
UPDATE — root cause was on our side, not a stuck check.
After capturing a HAR of real dev-store clicks, we found the actual cause: our app never emitted a same-origin “Authorization: Bearer” session-token request. It’s server-rendered (@shopify/shopify-app-react-router) and offloads its dynamic data calls to a separate, cross-origin backend — so it authenticated fine via the id_token URL parameter on document loads, but never made the client-side same-origin fetch that App Bridge decorates with the Bearer header. That header is the signal this embedded check looks for, so it stayed red even though auth worked end-to-end.
Fix: fire one authenticated same-origin request on the initial embedded load (e.g. GET /app/session-check) that carries the session token as an Authorization: Bearer header — using App Bridge’s idToken(). Backed by a tiny route that calls authenticate.admin(). Now every load emits the Bearer header the check inspects for.
If you’re a server-rendered app stuck on this exact check: don’t assume it’s a platform bug (that’s the trap we fell into). Capture a HAR of your dev-store clicks and confirm you actually emit an “Authorization: Bearer” header on a request to your OWN backend. If you don’t, that’s very likely why.
(We’ve deployed this fix and are awaiting the automated check / App Review to re-verify; will confirm here once it clears.) Automated check has made it through 10 cycles since the fix — and still not clearing.
Hey @Vestfold, really appreciate the follow up with the solution you found. That should help a lot of other developers that run in to similar roadblocks.
Thank you but please be aware that the problem of the blocked embedded app check using session tokens for user authentication still remains and needs a manual clear by Shopify. We are stuck and cannot submit the app.
The embed app check typically can’t be manually cleared. In most cases, installing your app in a new development store and clicking around in your app in the admin should clear that check.
If you do have problems after that though, let us know.
Thanks, Kyle — I did exactly that. I installed the app fresh on a brand-new development store and clicked through all the admin pages (Home, Reports, Settings, etc.).
Beforehand I also deployed a fix so the app now emits the Authorization: Bearer session-token header on load — it previously never made a same-origin Bearer request (it’s a server-rendered app that offloaded its live calls to a cross-origin backend, so it authenticated via the id_token URL parameter on document loads but never sent the Bearer header the check looks for).
I’ll report back after the next check cycle whether the check clears. Thanks again for the pointer.
Update, Kyle — following your suggestion I installed the app fresh on a new development store and clicked through every page. I also reworked the app so it now makes genuine same-origin session-token requests to its own backend on every page: the client fetches our own endpoints with the session token as an “Authorization: Bearer” header (via App Bridge idToken()), verified returning 200 in the Network tab (initiator app-bridge.js), rather than relying on document-load token exchange alone.
Many cycles later, “Using session tokens for user authentication” is still red (the App Bridge script check is green).
So the app now implements session-token auth exactly as the docs describe, and the fresh-install approach didn’t clear it. Is there something specific the automated check needs to observe that a server-rendered app doesn’t organically produce — or can this be manually verified? I also have an open Support ticket (#68744975). App ID 389108826113. Thanks for any pointer.
RESOLVED — posting the root cause because it will bite others.
The check is fed by App Bridge’s browser telemetry (beacons to monorail-edge.shopifysvc.com). My machine’s VPN (Proton NetShield) was blocking that domain at the DNS level — nslookup returned NXDOMAIN locally while resolving fine on 1.1.1.1/8.8.8.8. So every test session I ran sent zero telemetry to Shopify, and the check could never see our session-token activity, no matter how correct the implementation was. Incognito/extensions-off didn’t help because VPN DNS filtering sits below the browser.
Fix: disable the VPN (or its ad/tracker blocking), click through the app on a dev store, wait a cycle or two. The check went green and I’ve submitted.
If your session-token check is stuck red despite a verified-correct app: open DevTools Network while using your app in the admin, filter for “monorail” or “produce” — if those requests fail, your telemetry is being blocked client-side (ad-blocker, VPN, DNS filter) and the check is blind to you. Thanks KyleG for the pointers along the way.
Thank you for sharing this. This is a question we get quite often, so I’m going to add testing without a VPN as another potential blocker.