Skip to content

[Feature] Add token exchange auth strategy and hooks support to shopify-app-express#3097

Open
mrmarufpro wants to merge 4 commits into
Shopify:mainfrom
mrmarufpro:main
Open

[Feature] Add token exchange auth strategy and hooks support to shopify-app-express#3097
mrmarufpro wants to merge 4 commits into
Shopify:mainfrom
mrmarufpro:main

Conversation

@mrmarufpro

@mrmarufpro mrmarufpro commented Mar 13, 2026

Copy link
Copy Markdown

WHY are these changes introduced?

Fixes #630, Fixes #3208 — adds token exchange authentication support to shopify-app-express, bringing it to parity with the Remix package.

Shopify's token exchange API reduces flickering and OAuth redirects for embedded apps. While this was already supported in the Remix package, Express apps had no way to use it. This PR implements token exchange for Express behind the unstable_newEmbeddedAuthStrategy future flag so existing apps are unaffected.

WHAT is this pull request doing?

  • future config option — adds a feature flag system (unstable_newEmbeddedAuthStrategy, expiringOfflineAccessTokens) with logging for disabled flags
  • Token exchange middleware (perform-token-exchange.ts) — when unstable_newEmbeddedAuthStrategy is enabled and a Bearer token is present in the Authorization header, validateAuthenticatedSession performs a token exchange instead of redirecting to OAuth
  • ensureInstalled shortcut — when the token exchange strategy is on, ensureInstalledOnShop skips the session check and goes straight to embedding/loading the app
  • hooks.afterAuth — async callback invoked after both OAuth and token exchange flows, deduplicated via IdempotentPromiseHandler to prevent double-firing on concurrent requests
  • registerWebhooks({session}) — convenience method on ShopifyApp wrapping api.webhooks.register
  • expiringOfflineAccessTokens flag — threads the flag into auth.callback and auth.tokenExchange calls
  • ensureOfflineTokenIsNotExpired helper — proactively refreshes offline tokens nearing expiry in the standard OAuth path
  • Docs updated for shopifyApp, validateAuthenticatedSession, auth, and a new token-exchange guide added
  • Tests added for all new code paths

Type of change

  • Minor: New feature (non-breaking change which adds functionality)

Checklist

  • I have used pnpm changeset to create a draft changelog entry (do NOT update the CHANGELOG.md files manually)
  • I have added/updated tests for this change
  • I have documented new APIs/updated the documentation for modified APIs (for public APIs)

@mrmarufpro

Copy link
Copy Markdown
Author

I've been running these changes in a production embedded app by patching the package locally, and everything is working as expected. Token exchange flows complete cleanly, afterAuth fires correctly (including on concurrent requests, thanks to IdempotentPromiseHandler), and ensureInstalledOnShop behaves properly under the unstable_newEmbeddedAuthStrategy flag.

I did need a couple of minor tweaks to get it wired up in my app, but nothing that points to a problem with the implementation itself — just the usual integration details. Overall the behavior is solid and consistent with how the Remix package handles token exchange.

Happy to share more specifics if it helps the review.

@mrmarufpro

Copy link
Copy Markdown
Author

Hi @lizkenyon, I’m requesting a review.

@mrmarufpro mrmarufpro force-pushed the main branch 2 times, most recently from 7cafd76 to b15adad Compare April 13, 2026 03:55
@mrmarufpro mrmarufpro force-pushed the main branch 4 times, most recently from f449547 to 65f1035 Compare April 20, 2026 16:52
@mrmarufpro mrmarufpro force-pushed the main branch 2 times, most recently from 781885c to 0788067 Compare May 3, 2026 07:41
@dansundy

dansundy commented May 8, 2026

Copy link
Copy Markdown

May I ask if this is going to be released soon?

We're now required to use expiring offline access tokens so there doesn't seem to be a way to cleanly use this library with new apps.

@mrmarufpro

Copy link
Copy Markdown
Author

May I ask if this is going to be released soon?

We're now required to use expiring offline access tokens so there doesn't seem to be a way to cleanly use this library with new apps.

Hi @dansundy, I’m also looking forward to the review.

In the meantime, you can patch shopify-app-express in your project using the changes from this PR. It specifically adds support for the new token exchange strategy and those mandatory expiring offline access tokens you mentioned. Let me know if you run into any issues while testing it out!

@dansundy

dansundy commented May 8, 2026

Copy link
Copy Markdown

Thanks @mrmarufpro. Will do!

@kelreel

kelreel commented May 28, 2026

Copy link
Copy Markdown

hi! wen merge?

@mrmarufpro

Copy link
Copy Markdown
Author

Hi @kelreel, thanks for following up. The PR is currently awaiting review from the Shopify maintainers.

If you'd like to use the feature before it's merged, you can apply the changes to your installed @shopify/shopify-app-express using patch-package or pnpm patch. Please note that the published package ships compiled JavaScript in dist/, so the patch should target the built output rather than the src/ files shown in the PR.

Let me know if you run into any issues, and I'll be glad to help.

@kelreel

kelreel commented Jun 2, 2026

Copy link
Copy Markdown

This feature is urgently needed for app developers.

Why are critical platform changes being rolled out before the official libraries are ready?

https://shopify.dev/changelog/expiring-offline-access-tokens-required-for-all-public-apps-as-of-january-1-2027

Сould you please review this and complete the feature @tobi @mkevinosullivan @byrichardpowell ?

mrmarufpro and others added 4 commits June 21, 2026 09:43
… support

Implements the `unstable_newEmbeddedAuthStrategy` future flag which enables
token exchange-based authentication for embedded apps, bypassing the OAuth
redirect flow when a Bearer session token is present. Also adds:

- `future` config option with feature flag support and logging
- `hooks.afterAuth` callback invoked after OAuth and token exchange flows
- `registerWebhooks` convenience method on the ShopifyApp object
- `idempotentPromiseHandler` to deduplicate concurrent hook invocations
- `expiring` offline access token support in auth callback and token exchange
- `ensureOfflineTokenIsNotExpired` helper to refresh tokens nearing expiry

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ession invalidation

- Extract session token helpers (header + URL param) into get-session-token.ts
- Add respondToInvalidSessionToken helper with retry-header support
- Add invalidateAccessToken helper to clear stale access tokens from storage
- Send X-Shopify-Retry-Invalid-Session-Request:1 header on JWT/subject-token errors
- Invalidate stored access token on Shopify 401 responses to force re-exchange
- Fix URL parsing to use new URL().hostname instead of string replace
- Update tests to assert retry header and access-token invalidation behaviour
- Fix docs example to await shopify.registerWebhooks()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@kelreel

kelreel commented Jun 21, 2026

Copy link
Copy Markdown

@tobi @charlesdobson

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.

Add support for expiring offline access tokens to shopify-app-express Add support for token exchanger in Express

3 participants