---
title: Authenticate every request
description: >-
  Authenticate every request before you read or write store data, and take the
  store from the result of that authentication, never from the request itself.
source_url:
  html: >-
    https://shopify.dev/docs/apps/build/security/backend/authenticate-every-request
  md: >-
    https://shopify.dev/docs/apps/build/security/backend/authenticate-every-request.md
---

# Authenticate every request

Authenticate every request before you read or write store data, and take the store from the result of that authentication, never from the request itself.

***

## Why

Your shipped JavaScript, including theme app extension assets and embedded app bundles, names every endpoint that it calls, so every endpoint is discoverable. An endpoint that accepts the store from a `shop` parameter or body field lets any caller act as any store that has your app installed. Endpoints that save settings your storefront code renders, such as widget settings or custom CSS, JavaScript, or HTML, are the highest-impact case, because one request puts a script on a storefront.

Expiring access tokens and API log reviews don't detect this. Your own server makes the follow-up GraphQL Admin API call with a valid token, so the request looks like normal traffic.

***

## How

Match the authentication method to where the request comes from, then reject anything that doesn't verify.

### Match the method to the request

| Request comes from | Authenticate it with | In the React Router app template |
| - | - | - |
| Your embedded app in the Shopify admin | [ID token](https://shopify.dev/docs/apps/build/authentication-authorization/id-tokens) | [`authenticate.admin`](https://shopify.dev/docs/api/shopify-app-react-router/latest/authenticate/admin) |
| A storefront, including JavaScript in your theme app extension | [App proxy signature](https://shopify.dev/docs/apps/build/online-store/app-proxies/authenticate-app-proxies) | [`authenticate.public.appProxy`](https://shopify.dev/docs/api/shopify-app-react-router/latest/authenticate/public/app-proxy) |
| A checkout or customer account UI extension | The extension's session token | [`authenticate.public.checkout`](https://shopify.dev/docs/api/shopify-app-react-router/latest/authenticate/public/checkout) or [`authenticate.public.customerAccount`](https://shopify.dev/docs/api/shopify-app-react-router/latest/authenticate/public/customer-account) |
| A Shopify webhook | [HMAC signature](https://shopify.dev/docs/apps/build/webhooks/verify-deliveries) | [`authenticate.webhook`](https://shopify.dev/docs/api/shopify-app-react-router/latest/authenticate/webhook) |

### Reject what doesn't verify

* If a request has no valid token or signature, then reject it before doing anything else. Don't fall back to a `shop` value from a parameter, body field, header, or cookie.
* An app proxy signature proves which store a request came through, not who sent it, so don't let app proxy routes change store settings. For actions on behalf of a logged-in customer, use the `logged_in_customer_id` value from the signed request.
* A request that your storefront JavaScript sends directly to your server, rather than through an app proxy, carries no proof of which store it came from. Treat it as anonymous: it can read public data, but it must not change anything for a store.
* Debug, test, migration, and legacy routes need the same authentication. If you don't need a route, then remove it.

### If you don't use a Shopify library

* Validate every part of the ID token: signature, expiry, audience, and destination claims.
* Verify webhook HMACs against the raw request body before parsing, and treat webhook headers, including the topic and shop domain, as untrusted until the signature verifies. Make webhook handling idempotent, and use delivery IDs to detect replays.
* Bind the OAuth `state` value to the browser that started the flow, and check it on return. Require exact matches against your approved redirect URLs.
* Validate a shop domain against a pattern that's anchored at both ends, so that a value such as `example.myshopify.com.attacker.net` doesn't match.

For details, see [authentication and authorization](https://shopify.dev/docs/apps/build/authentication-authorization) and [verify webhook deliveries](https://shopify.dev/docs/apps/build/webhooks/verify-deliveries).

***
