Every Shopify app that reads orders, customers or products does it with an access token, and a leaked or over-scoped token gives whoever holds it the same access to the store's data as the app. Shopify has changed its token model significantly: expiring offline tokens with refresh tokens, token exchange for embedded apps, a client credentials grant for merchant-owned integrations, and the end of non-expiring offline tokens for new public apps. This guide explains the token types, access scopes, staff permissions that control who can install and build apps, how to store, rotate and revoke tokens, and what to log. It is for merchants' security and engineering teams, agencies building custom apps, and app developers handling store credentials. Facts are taken from Shopify's developer and help documentation, checked on 25 October 2026.
Key takeaways
- Tokens are bearer credentials. Admin API tokens (prefix
shpat_) are sent in theX-Shopify-Access-Tokenheader and work for anyone who holds them, so they belong in a secrets manager, never in code, themes or tickets. - Prefer expiring tokens. Expiring offline tokens last one hour with a 90-day refresh token; non-expiring offline tokens last until uninstall or secret revocation and are being withdrawn for public apps.
- Scopes are the blast radius. Write scopes include read,
read_all_ordersopens order history beyond 60 days, and protected customer data needs separate approval: request the minimum and check what was actually granted. - The client secret is the master key. It signs webhooks, mints tokens and, when revoked, kills every token pinned to it; rotation is a five-step process that must be rehearsed.
- Staff permissions gate app risk. Who holds "Manage and install apps and channels" and "Develop" decides who can hand store data to a third party.
How identity and access work in Shopify apps
Shopify apps are non-human identities with three moving parts: the app's client ID and client secret (configured in the Dev Dashboard or Partner tooling), the access scopes declared in the [access_scopes] section of the app configuration, and the access token issued per store at installation. Tokens are obtained with a POST to https://{shop}.myshopify.com/admin/oauth/access_token using one of three grants.
| Token type | Lifetime | Tied to | Typical use | Security notes |
|---|---|---|---|---|
| Offline, expiring | 1 hour (expires_in: 3600); refresh token 90 days (refresh_token_expires_in: 7776000) | The store installation | Background jobs, webhooks, sync | Store refresh tokens as secrets; refreshing can shorten the remaining window |
| Offline, non-expiring | Until the app is uninstalled or its client secret is revoked | The store installation | Legacy public apps, custom and merchant-created apps | New public apps cannot use them for the GraphQL Admin API; existing public apps lose that after 1 January 2027 |
| Online | 24 hours or until the staff member logs out, whichever is first | A staff member (associated_user) | Interactive admin actions that should respect the user's permissions | Requests the user cannot perform return ACCESS_DENIED; logout revokes the session's online tokens |
Delegate (shppa_) | Parent token's remaining lifetime or a shorter expiresIn | Derived from an offline token | Handing a narrower token to a sub-service | Limited to the scopes you specify: use it to keep full tokens out of less trusted components |
| Client credentials | 24 hours (expires_in: 86399), renewed by repeating the request | Your own stores | Server-side integrations with no merchant interaction | Whoever has the client secret can mint tokens; protect the secret, not just the token |
| ID token (formerly session token) | About one minute | The signed-in user in an embedded app | Proving the request came from an authenticated Shopify user | Carries no permissions and cannot call any API; it is exchanged for an access token |
The three grants
- Token exchange: used by embedded apps. The backend sends the ID token as
subject_token;expiring=1requests an expiring offline token. An expired ID token returns400, so fetch a fresh one for each exchange. - Authorization code grant: used by standalone apps outside the Shopify admin. Shopify's documentation requires four checks before trusting the callback: verify the
hmacparameter (HMAC-SHA256 over the sorted parameters with the client secret), confirm thestatenonce matches the signed cookie, validate thatshopends inmyshopify.comand contains only letters, numbers, periods and hyphens, and compare the grantedscopewith what the app needs. - Client credentials grant: for integrations acting on stores you own, with
grant_type=client_credentials. It suits back-office sync jobs and avoids storing a long-lived token, at the cost of making the client secret itself the credential to protect. See the Machine-to-Machine OAuth (Client Credentials) Guide.
Validating ID tokens
If an embedded app validates ID tokens itself, Shopify specifies checking the HS256 signature with the client secret, exp in the future, nbf in the past, aud equal to the app's client ID, and matching hostnames in iss and dest, returning 401 before calling the token endpoint if any check fails.
Access scopes
Scopes decide what a token can touch. Shopify's guidance is to request only the data the app needs, and it restricts scopes for apps without a legitimate use for the data.
| Scope family | Examples | Acts as | Risk notes |
|---|---|---|---|
| Authenticated (Admin API) | read_products, write_products, read_orders, write_orders | The app on behalf of a store user | Any write scope also grants read; request write_ only if both are needed |
| Order history | read_all_orders | Extends read_orders or write_orders | Order scopes cover only the last 60 days by default; this opens the full history and requires Shopify approval for Partner apps |
| Approval-required | read_customer_payment_methods, read_shopify_payments_dispute_evidences, read_users | The app | Declaring them without approval fails app version creation with an app_access error |
| Unauthenticated (Storefront API) | unauthenticated_read_product_listings, unauthenticated_write_checkouts | A customer on the storefront | Lower sensitivity, but still scope to what the storefront needs |
| Customer Account API | customer_read_orders, customer_read_customers | Data of the signed-in customer | Customer personal data: apply the protected data rules |
Protected customer data
Apps get no protected customer data by default. Declaring the scope is not enough: the API returns no such data from live stores until the app is configured and approved, in a review separate from scope approval. Shopify defines Level 1 as customer data without name, address, email or phone, and Level 2 as data including any of those fields. Level 1 requirements include data minimisation, purpose limitation, retention periods and encryption at rest and in transit. Level 2 adds encrypted backups, separation of test and production data, a data loss prevention strategy, limited staff access, strong staff passwords, an access log of protected customer data access and a security incident response policy. Shopify may request evidence in a data protection review, and apps with many installs or broad field access are more likely to be selected.
Optional scopes drift
Optional scopes are granted after installation, so an installation's real scopes can differ from the configuration. Query currentAppInstallation and its accessScopes field, and subscribe to the app/scopes_update webhook, which fires whenever any installation's scopes change, to keep an accurate record.
Admin roles and privileged access
On the merchant side, the risk of app tokens is controlled by staff permissions in the Shopify admin. The permissions that matter most:
| Permission (Shopify admin) | What it allows | Who should hold it |
|---|---|---|
| Manage and install apps and channels | Install apps, which approves their requested scopes for the store | A small named group; every install is a data-sharing decision |
| Approve app charges | Install paid apps or themes, including trials that become paid | Finance-aware approvers |
| App development: Develop | Create and update custom apps installed on the store | Named developers; review membership quarterly |
| App development: Enable development | Permit others to develop custom apps (also requires Manage and install apps and channels) | Store owner or platform lead only |
| App development: View apps developed by staff and collaborators | See custom apps on the store | Security and audit staff |
- Store owner: has full access to create and manage custom apps automatically, so protect the owner account first.
- Two-step authentication: options include authenticator apps, security keys, built-in authenticators and passkeys; SMS cannot be set up as a new method. Staff enable it on their own accounts and the owner cannot switch it on for them. Shopify's help documentation describes Shopify Plus stores as able to require a secure sign-in method for all users in the organisation.
- Collaborators: Partner collaborator accounts work on the store with permissions limited to the sections granted. They cannot access the Dev Dashboard. Each custom app is associated with a staff or collaborator account that receives its warnings, viewable on the app's settings page; reassign it when that person leaves.
Integrations, API tokens and service accounts
Storing tokens
- Location: keep access tokens, refresh tokens and the client secret in a secrets manager or an encrypted database column with keys held in a KMS, never in theme code, front-end bundles, repository files or environment files committed to Git (the ATT&CK T1552.001 Credentials In Files pattern). See the Secrets Management Guide.
- Isolation: one token per store, keyed by the validated shop domain; never let a request parameter choose which store's token is used without checking the session.
- Narrowing: give workers that only need product data a delegate token with product scopes rather than the store's full offline token.
- Detection of leaks: enable secret scanning in repositories and CI for the
shpat_andshppa_prefixes. See the Git Secret Scanning Guide.
Rotating and revoking
Tokens are pinned to the client secret that minted them, which makes secret rotation the main revocation lever. Shopify's documented sequence in the Dev Dashboard (Settings, Credentials):
- Select Rotate, then Generate new secret. The old secret stays active.
- Update webhook verification to accept signatures from both secrets; Shopify keeps signing with the oldest unrevoked secret, and the HMAC can take up to an hour to reflect the new one.
- Switch token exchange, authorization code and refresh requests to the new secret.
- Replace tokens pinned to the old secret: repeat token exchange, request new client credentials tokens, refresh expiring offline tokens with
grant_type=refresh_token, and for non-expiring offline tokens use Generate refresh token (a one-hour rotation refresh token) and re-pin each store's token. The old access token is destroyed, so store each response before moving on. - Revoke the old secret once every stored token is re-pinned; tokens still pinned to it stop working. Then verify webhooks against the new secret only.
Emergency exception: if the secret is being actively misused, Shopify advises revoking it immediately, before generating a new one, accepting that merchants lose access until the new secret is deployed. Retry a timed-out refresh promptly with the same refresh token; Shopify returns the same rotated credentials. Shopify's rotation page does not document an endpoint for revoking a single access token, so plan containment around secret rotation and app uninstall.
Uninstall and offboarding
- Merchants: uninstalling an app ends a non-expiring offline token's validity. Uninstall apps that are no longer used and delete unsupported custom apps; treat this as part of supplier offboarding. See the NHI Offboarding and Decommissioning Guide.
- Developers: subscribe to
app/uninstalledand delete the store's tokens when it fires. Public apps must also handle the mandatory compliance webhookscustomers/data_request,customers/redactandshop/redact;shop/redactarrives 48 hours after uninstall, and the required action must be completed within 30 days.
Logging and detection
| Signal | Source | Why it matters |
|---|---|---|
| Scope changes on an installation | app/scopes_update webhook; currentAppInstallation.accessScopes | Unexpected new scopes widen the blast radius |
| Uninstalls | app/uninstalled webhook | Triggers token deletion; uninstalls you did not expect may indicate a merchant-side incident |
| Webhook HMAC failures | Your endpoint, X-Shopify-Hmac-SHA256 mismatches | Forged deliveries or a misapplied rotation; return 401 and alert on spikes |
| Duplicate deliveries | X-Shopify-Webhook-Id | Idempotency and replay detection |
| OAuth callback check failures | Your app logs (hmac, state, shop hostname, scope mismatch) | Probing or CSRF attempts against the install flow |
| Token use from your infrastructure | Your egress logs and API client logs per store | Calls from unknown hosts or sudden bulk reads of orders and customers suggest a stolen token |
| Protected customer data access | Your application access log (required at Level 2) | Evidence for data protection reviews and incident scoping |
| Staff permission changes | Shopify admin user management and periodic review | New holders of app install or develop permissions |
Metrics
- Percentage of store tokens that are expiring offline tokens or client credentials tokens rather than non-expiring.
- Number of installed apps with write or
read_all_ordersscopes that lack a named owner and recent review. - Days since the last client secret rotation, and measured duration of the last rehearsed rotation.
- Count of staff holding Manage and install apps and channels or Develop, against an agreed maximum.
- Time from
app/uninstalledto token deletion in your store of record.
Hardening baseline
Phased rollout for an app or integration team
- Inventory: list every store installation, token type, granted scopes (from
accessScopes) and where each token is stored. Search repositories and logs forshpat_. - Move secrets: migrate tokens and the client secret into a secrets manager; rotate anything found in code or logs.
- Adopt expiring tokens: request
expiring=1in token exchange or the authorization code grant; test refresh handling, retries and the 90-day refresh expiry on a development store first. - Trim scopes: drop unused write scopes and
read_all_orders, ship as a new app version, and confirm withapp/scopes_update. - Rehearse rotation: run the five-step secret rotation on a staging app, time it, and document the emergency revoke path.
- Rollback: keep the old secret active until every token is re-pinned; if refresh logic fails, pause the switch, since revoking early is the step that cuts merchants off.
Decision guidance
- Merchant-owned back-office integration: prefer the client credentials grant with 24-hour tokens over a stored non-expiring token, and protect the client secret in a secrets manager.
- Embedded public app: token exchange with expiring offline tokens; use online tokens for actions that should respect the individual staff member's permissions.
- Small merchant: focus on few installers, two-step authentication on every staff account, and a quarterly uninstall review. Large or Plus merchant: require secure sign-in organisation-wide, document an owner per app and include apps in third-party risk reviews. See the SaaS and OAuth App Governance Guide.
Common misconfigurations
- Tokens in theme or front-end code: an Admin API token placed in theme JavaScript or a mobile bundle is public. Only Storefront API access belongs client-side.
- Write scopes "just in case": requesting
write_orderswhen the app only reads orders doubles the damage a leaked token can do. - Trusting the shop parameter: skipping hmac and hostname validation lets an attacker steer the install flow or token lookup to another store.
- Revoking the old secret first: outside a confirmed misuse, this cuts every merchant off; the documented order is generate, dual-verify, re-pin, then revoke.
- No uninstall handling: tokens and customer data kept after
app/uninstalledandshop/redactbecome an unmanaged liability. - Integration vendors as single points of failure: as with the Salesloft Drift breach, where OAuth tokens held by one integration vendor were used to export data from hundreds of Salesforce tenants, a vendor holding tokens for many stores concentrates risk; ask how tokens are stored and how fast they can be revoked.
Framework mapping
| Framework | Reference | Shopify app token control |
|---|---|---|
| NIST SP 800-53 Rev. 5 | AC-6 Least Privilege | Minimal scopes, delegate tokens, few app installers |
| NIST SP 800-53 Rev. 5 | IA-5 Authenticator Management | Client secret and token storage, rotation, expiry |
| NIST SP 800-53 Rev. 5 | AC-2 Account Management; IA-2 | Staff accounts, collaborator access, two-step authentication |
| NIST SP 800-53 Rev. 5 | AU-2, AU-12 | Webhook, callback and protected data access logging |
| NIST SP 800-53 Rev. 5 | SC-8, SC-28 | Encryption in transit and at rest (Shopify Level 1) |
| NIST SP 800-53 Rev. 5 | SA-9 External System Services | Third-party app and integration vendor oversight |
| NIST CSF 2.0 | PR.AA-01, PR.AA-05, PR.DS-01, GV.SC-07 | App identities, permissions, data at rest, supplier risk |
| ISO/IEC 27001:2022 Annex A | 5.17 Authentication information; 5.19 Information security in supplier relationships; 8.15 Logging | Secret handling, app vendors, logs |
| MITRE ATT&CK v20 | T1528, T1552.001, T1199 | Token theft, tokens in files, abuse of a trusted app vendor |
For card data obligations that may sit alongside an e-commerce app, see the PCI DSS 4.0 Guide.
Practitioner checklist
- Inventory every app installation with token type, granted scopes and storage location.
- Move access tokens, refresh tokens and client secrets into a secrets manager; scan code for
shpat_andshppa_. - Request expiring offline tokens and plan the move off non-expiring tokens before 1 January 2027 for public apps.
- Remove unused write scopes and
read_all_orders; track changes withapp/scopes_update. - Enforce hmac, state, shop hostname and scope checks in the authorization code flow, and full claim checks on ID tokens.
- Verify every webhook HMAC with a constant-time comparison on the raw body.
- Delete tokens on
app/uninstalledand implement all three compliance webhooks. - Limit Manage and install apps and channels, Develop and Enable development to named staff, with two-step authentication on every account.
- Rehearse client secret rotation and the emergency revoke path at least annually.
Standards and references
- Shopify: About access tokens
- Shopify: Access scopes
- Shopify: Rotate or revoke client credentials
- Shopify: Authorization code grant
- Shopify: ID tokens
- Shopify: Protected customer data
- Shopify: Deliver webhooks through HTTPS
- Shopify Help Center: Store permissions
Related NHI Mgmt Group resources: SaaS API Token Security Guide · Webhook Secrets Security Guide · Stripe API Keys Security Guide · MITRE ATT&CK T1528 Steal Application Access Token Guide · Retail and E-Commerce Identity Security Guide