Authentication
OpenShop uses a different authentication mechanism for each surface. Do not forward one surface’s credential to another.
| Surface | Credential | Verification | Trusted identity |
|---|---|---|---|
Embedded admin and /api/* |
Shopify session token in Authorization: Bearer … |
HMAC JWT signature, audience, time claims, and destination | shop, shopifyApp, staff user subject (sub) |
OAuth start /auth |
Shop domain and optional configured app handle | Shop normalization and configured app lookup | requested shop and app |
| OAuth callback | Signed Shopify query plus OAuth state |
Shopify HMAC and stored nonce | verified shop and app |
/webhooks/* |
X-Shopify-Hmac-Sha256 |
HMAC over the raw request body | shop and app selected by the matching secret |
/proxy/* |
Shopify App Proxy query signature, or Customer Account JWT | Query HMAC or JWT | shop, app, optional customer |
/ext/* |
Customer Account session JWT | JWT signature and audience | shop, app, customer |
/routes/* |
Route-defined | Explicit auth function, or intentionally public with auth: 'none' |
Route-defined; optional forShop() context |
/mcp |
OpenShop token in Authorization: Bearer … |
Stored token hash, status, expiry, shop, and permissions | token, shop, app, grants |
Embedded admin API
Section titled “Embedded admin API”Send the Shopify session token exactly as a bearer token:
Authorization: Bearer <shopify-session-token>OpenShop rejects missing, invalid, expired, or wrong-audience tokens with
401. API handlers read the verified shop and app identity from middleware;
client-supplied shop query parameters do not replace that identity.
OAuth installation
Section titled “OAuth installation”Single-app mode starts installation at:
/auth?shop=example.myshopify.comMulti-app mode also requires the configured handle:
/auth?shop=example.myshopify.com&app=clientAThe callback validates the HMAC and one-time state before storing the access
token. Access tokens are encrypted when ENCRYPTION_KEY is configured.
Webhook and proxy bodies
Section titled “Webhook and proxy bodies”Webhook HMAC validation uses the raw body. Reverse proxies and middleware must
not rewrite it before OpenShop receives the request. App Proxy signatures cover
the signed query parameters. In proxy handlers, trust ctx.shop,
ctx.shopifyApp, and ctx.customerId; do not replace them with values from
ctx.query.
Browser origins
Section titled “Browser origins”OpenShop allows known Shopify origins, configured app origins, local development
origins, and https://extensions.shopifycdn.com. Arbitrary origins are not
reflected in CORS responses.
See Security for secret handling and deployment requirements.
App selection
Section titled “App selection”| Request | App resolution |
|---|---|
| Manual OAuth start | ?app=<handle>; optional when exactly one app exists |
| Signed admin launch or OAuth callback | Query HMAC matches exactly one app secret |
| App proxy | Query signature matches exactly one app secret |
| Webhook | Raw-body HMAC matches exactly one app secret |
| App Bridge or Customer Account JWT | Audience matches exactly one API key, then signature verification |
Zero or ambiguous matches fail authentication. Configured API keys must be unique, and each app should use its own secret. See Configure Shopify apps for setup and Shop and app isolation for the data boundary.