Journal
Token Exchange killed the OAuth redirect
Shopify Token Exchange and Managed Installation change OAuth app setup. See what to delete, what to build, and which migration gotchas matter.
Key takeaways
What to remember
- Managed Installation shifts install authorization away from custom OAuth redirect handling.
- Token Exchange gives embedded apps a safer way to obtain app access tokens.
- The migration is mostly deleting old redirect assumptions and tightening app-session logic.
There’s a tab open in your browser right now that loads
https://your-app.com/auth?shop=...&hmac=...×tamp=.... If you wrote that
route after January 2025, you wrote dead code.
The new model — Managed Installation plus Token Exchange — has been the recommended path for embedded Shopify apps for almost a year. Most tutorials still teach the redirect flow because Shopify hasn’t taken the old docs down. They should. The migration is shorter than people think, and the result is simpler in every direction.
What changed
Managed Installation moved the install + consent screen into Shopify itself.
You declare the scopes your app needs in shopify.app.toml, push it with
shopify app deploy, and Shopify handles the install UX. No redirect. No
state nonce. No HMAC-on-querystring dance.
App Bridge — the JS SDK that runs inside the Admin iframe — mints a session token (a JWT) on every request from the embedded UI. Your backend takes that JWT and exchanges it for an offline access token via the Token Exchange endpoint. The offline token is what you use for every subsequent Shopify API call.
End state: no /auth route, no /auth/callback, no nonces, no install-flow
cookies, no post-install redirect logic. Just middleware.
What you delete
- The
/authinstall URL builder - The
/auth/callbackhandler - The HMAC verifier on the redirect query string (you still need HMAC on webhooks — don’t delete that one)
- The cookie-based pre-auth session
- The post-install redirect URL builder
- The
statenonce store
Most of this lives in one or two files. Deleting it is satisfying.
What you write instead
Middleware. About 80 lines of Go. The shape:
- Pull the JWT out of the
Authorization: Bearer …header. - Validate the signature against your app secret.
- Verify the audience and issuer claims — both must match the shop domain making the request.
- Look up an offline token for that shop in Redis.
- If miss: call Token Exchange with the JWT as
subject_token, cache the returned offline token under{app}:token:{shop_domain}. - Hand the offline token to the request context.
That’s it. One round-trip per uncached shop, then everything is local.
Gotchas nobody warns you about
Session tokens expire after 60 seconds. App Bridge auto-refreshes them on the client. Server-side webhook handlers don’t have App Bridge. They still need HMAC verification — do not delete that path.
invalid_subject_token is a soft error. Token Exchange returns it when a
shop has uninstalled mid-flight, or when the JWT is past its 60-second window.
Treat it as a 401 and let the client retry. Treating it as a 500 will fill
your error tracker with noise during normal uninstalls.
The offline token can be revoked out-of-band. A merchant can uninstall and
reinstall in the same minute. Your cache won’t know. The right invalidation
trigger is “next failed API call returns invalid_token” — not the install
webhook, which can race.
Local dev still uses the live Shopify issuer. Even when you’re tunneling to localhost, the JWT issuer is the production Shopify domain. Don’t hardcode issuer checks against your tunnel URL.
When NOT to migrate
If your app is non-embedded — lives outside the Admin iframe and serves its own dashboard — OAuth redirect is still the right tool. App Bridge isn’t there to mint session tokens, so there’s nothing to exchange. Our event pipeline app stays on OAuth redirect for exactly this reason. Embedded apps are the ones that benefit.
Recurrabee and searchabee both run this pattern in production today — see /work for what they actually do. The migration took a couple of afternoons each. The auth code that’s left is shorter, easier to reason about, and stops being something you have to think about.
Frequently asked questions
Does Shopify still require OAuth redirects for embedded apps?
Modern managed-installation apps can use Token Exchange instead of maintaining a full legacy OAuth redirect flow.
What should be removed during a Token Exchange migration?
Remove old redirect handlers, callback-state assumptions, and install-path logic that conflicts with Shopify managed installation.
What is the biggest Token Exchange migration risk?
The main risk is keeping mixed legacy and modern flows alive, which can create broken installs or confusing session behavior.