ShopifyAutoAuth
Enterprise-Grade Shopify OAuth 2.0 Client Credentials Adapter
A zero-friction, production-hardened bridge connecting legacy Shopify applications to modern Client Credentials Grant authentication.
Overview
Following Shopify's mandatory authentication migration on January 1, 2026, static shpat_xxx custom app tokens were deprecated in favor of OAuth 2.0 Client Credentials Grant short-lived access tokens (RFC 6749 §4.4).
shopify_auth_adapter provides a transparent authentication proxy. Existing applications can preserve their variable assignment patterns while under the hood tokens are acquired, cached in volatile RAM, and refreshed prior to expiration without runtime interruption.
Migration: Before vs After
# BEFORE (Deprecated static token): SHOPIFY_ACCESS_TOKEN = "shpat_xxxxxxxxxxxxxxxxxxxxxxxx" # AFTER (Automatic OAuth 2.0 token with auto-refresh): from shopify_auth_adapter import get_access_token SHOPIFY_ACCESS_TOKEN = get_access_token()
⚡ Quick Start: 1 Line of Code
To upgrade any Python application to Shopify's modern OAuth 2.0 authentication, replace your deprecated shpat_xxx string with get_access_token():
from shopify_auth_adapter import get_access_token # Automatically fetches, caches in RAM, and auto-refreshes OAuth 2.0 tokens: SHOPIFY_ACCESS_TOKEN = get_access_token()
That's it! Everything else (OAuth 2.0 requests, RAM caching, automatic 24h refresh) happens automatically behind the scenes.
Installation
pip install shopify-auth-adapter
Setting Up Credentials
You can supply your Shopify Dev Dashboard credentials in any of 3 ways:
Option A: In a .env file (Recommended for Local Dev)
Create a .env file in your project root:
SHOPIFY_SHOP=your-store.myshopify.com SHOPIFY_CLIENT_ID=your_client_id SHOPIFY_CLIENT_SECRET=your_client_secret
Option B: Environment Variables (Production / Docker / Cloud)
Set variables in your terminal or deployment environment:
export SHOPIFY_SHOP="your-store.myshopify.com" export SHOPIFY_CLIENT_ID="your_client_id" export SHOPIFY_CLIENT_SECRET="your_client_secret"
Option C: Pass Directly in Python Code
from shopify_auth_adapter import get_access_token token = get_access_token( shop="your-store.myshopify.com", client_id="your_client_id", client_secret="your_client_secret", )
Testing Credentials
You can verify your Dev Dashboard credentials and store connection instantly by running the included tester script:
python examples/test_credentials.py
Usage Examples
1. Drop-In Token Helper
import httpx from shopify_auth_adapter import get_access_token # Automatically reads credentials from .env or environment variables: headers = {"X-Shopify-Access-Token": get_access_token()} response = httpx.get( "https://your-store.myshopify.com/admin/api/2026-07/shop.json", headers=headers, )
2. High-Level Shopify Client
from shopify_auth_adapter import ShopifyClient # Automatically attaches tokens and retries on 401: client = ShopifyClient() # REST API call blogs = client.get("/blogs.json").json()["blogs"] # GraphQL API call shop_info = client.graphql(""" query { shop { name email } } """)
Feature Matrix
| Domain | Capability | Architectural Mechanism |
|---|---|---|
| Authentication | OAuth 2.0 Client Credentials Grant | Automatic POST /admin/oauth/access_token exchange for Dev Dashboard apps. |
| Concurrency | Thundering Herd Protection | Re-entrant threading.RLock double-checked locking mechanism. |
| Resilience | Clock-Skew Mitigation | 300-second (5-minute) proactive expiration cutoff buffer. |
| Header Integration | Transparent LiveToken Proxy |
Custom str subclass executing JIT encoding delegation during HTTP header resolution. |
| Client Abstraction | High-Level ShopifyClient |
Integrated REST & GraphQL client with automatic 401 cache-invalidation and single retry. |
| Security | Zero Credential Leak Guarantee | String masking on repr, str, and tracebacks (LiveToken(<masked>)). |
System Architecture & Domain Boundaries
The codebase follows domain-driven modularity principles with contract-based interfaces (Protocol) to guarantee zero coupling and total separation of concerns.
flowchart TD
subgraph AppLayer["Client Application Layer"]
App["Application Codebase"]
Env["Environment Config (.env)"]
end
subgraph PkgDomain["shopify_auth_adapter Package"]
Facade["API Facade"]
Config["ShopifyConfig"]
Proxy["LiveToken Proxy"]
Manager["TokenManager"]
Cache["InMemoryTokenCache"]
Provider["OAuth2ClientCredentialsProvider"]
Client["ShopifyClient"]
end
subgraph ExtInfra["External Infrastructure"]
ShopifyAuth["Shopify OAuth Endpoint"]
ShopifyAPI["Shopify Admin API"]
end
Env --> Config
App --> Facade
Facade --> Manager
Manager --> Provider
App --> Proxy
Proxy --> Manager
Manager --> Cache
Manager --> Provider
Provider --> ShopifyAuth
App --> Client
Client --> Proxy
Client --> ShopifyAPI
Request Lifecycle & Token Resolution
The diagram below illustrates the exact sequence executed when an HTTP request header formats the LiveToken string proxy:
sequenceDiagram
autonumber
participant App as Client Application
participant Proxy as LiveToken Proxy
participant Manager as TokenManager
participant Cache as InMemoryTokenCache
participant Provider as OAuth2Provider
participant Shopify as Shopify Server
App->>Proxy: Header resolution (.encode)
Proxy->>Manager: get_token()
Manager->>Cache: get()
alt Cache Hit (Valid Token)
Cache-->>Manager: CachedToken
else Cache Miss / Expired
Manager->>Manager: Acquire Lock
Manager->>Provider: fetch_token()
Provider->>Shopify: POST /admin/oauth/access_token
Shopify-->>Provider: 200 OK Token Response
Provider-->>Manager: access_token
Manager->>Cache: set()
end
Manager-->>Proxy: access_token string
Proxy-->>App: Encoded Token Bytes
Repository Structure
ShopifyAutoAuth/
├── src/
│ └── shopify_auth_adapter/ # Primary Python package
│ ├── auth/ # OAuth2 provider, token manager, LiveToken proxy
│ ├── cache/ # In-memory token cache implementation
│ ├── client/ # ShopifyClient REST and GraphQL helper
│ └── core/ # Configuration, constants, exceptions, protocols
├── docs/ # In-depth technical documentation
├── tests/ # Unit and integration test suite
├── .github/ # GitHub Actions workflows & community templates
├── pyproject.toml # Packaging & tool configurations
└── README.md # Project overview & documentation
Technical Deep Dives
Clock-Skew Math & Expiration Buffer
Shopify Client Credentials Grant tokens carry a fixed duration of 86,399 seconds (~24 hours).
To prevent edge cases where a token is valid when read from cache but expires while in flight across the network, the expiration cutoff is calculated as:
$$\text{Cutoff} = T_{\text{expiry}} - 300\text{ seconds}$$
Any token within 5 minutes of expiration is treated as invalid, forcing a fresh token fetch prior to request dispatch.
Security Invariants & Token Masking
Credentials and raw access tokens are strictly protected against unintentional exposure in logs, error reports, and inspection tools:
repr(LiveToken)returns"LiveToken(<masked>)".repr(ShopifyConfig)outputsclient_secret=<redacted>.repr(CachedToken)outputsaccess_token=<redacted>.- Exception messages exclude credential strings under all circumstances.
Development & Tooling Shortcuts
Common developer tasks are managed via the project Makefile:
# Install editable package with dev tools make install # Run linter and type checks make lint make typecheck # Run full test suite with coverage make test
Build & CI/CD Pipeline
The project utilizes GitHub Actions for continuous quality verification and release automation:
flowchart LR
Push["Code Push / Tag"] --> Lint["Ruff Linter"]
Push --> TypeCheck["Mypy Strict Check"]
Push --> Test["Pytest Suite"]
Lint --> Build["Hatchling Build"]
TypeCheck --> Build
Test --> Build
Build --> PyPI["Publish to PyPI Registry"]
License
This project is licensed under the terms of the MIT License.
Engineered by Ahmad Hassan (B-Ted) 💚