Shopify Auth
Add customer authentication with login, account pages, and nav integration.
How to use
Enable Authentication
Add customer authentication using better-auth with Shopify Customer Account API OIDC. This enables customer login, profile management, order history, and address book.
Prerequisites
- Shopify store with Customer Account API enabled (Shopify Admin → Settings → Customer accounts)
- Customer Account API credentials (client ID + client secret)
- A
AUTH_SECRETvalue for session signing (generate withopenssl rand -base64 32)
Required environment variables
| Variable | Description |
|---|---|
AUTH_SECRET | Secret for signing sessions (also known as BETTER_AUTH_SECRET) |
SHOPIFY_CUSTOMER_CLIENT_ID | Shopify Customer Account API client ID |
SHOPIFY_CUSTOMER_CLIENT_SECRET | Shopify Customer Account API client secret |
BETTER_AUTH_BASE_URL | App base URL (e.g. http://localhost:3000 for dev) |
SHOPIFY_STORE_DOMAIN | Already set — used for OIDC discovery |
Part 1: Core auth plumbing (always run)
Step 1. Install better-auth
Step 2. Update next.config.ts
Add better-auth to serverExternalPackages:
Step 3. Update turbo.json
Add auth env vars to globalEnv:
Step 4. Create lib/auth/auth.ts
Core better-auth configuration with Shopify OIDC:
Step 5. Create lib/auth/server.ts
Server-side session helpers with React cache for per-request memoization:
Step 6. Create lib/auth/client.ts
Client-side auth hooks and actions:
Step 7. Create app/api/auth/[...all]/route.ts
Shopify Admin setup
- Go to Shopify Admin → Settings → Customer accounts
- Enable Customer Account API
- Create a Customer Account API client (under "API clients")
- Set the redirect URI to
{YOUR_DOMAIN}/api/auth/callback/shopify - Copy the client ID and client secret to your environment variables
- Ensure the store domain matches
SHOPIFY_STORE_DOMAIN
Guardrails
- Never expose access tokens to the client —
getSession()andrequireSession()are server-only - Always call
requireSession()before any customer API operation - The Customer Account API uses a separate GraphQL endpoint from the Storefront API — validate fields with
shopify-ai-toolkitorvercel-shop:fetch-shopify-schema - Session cookies use
httpOnlyandsecureflags automatically via better-auth - PKCE is enabled for the OAuth flow — never disable it
After completing Part 1, ask the user:
If the user chooses "Skip UI for now", stop here — Part 1 is complete and functional for programmatic use (server actions, API routes, session checks).
If the user chooses "Yes, create the full UI", proceed with Part 2.
Part 2: Customer-facing UI (only if user opted in)
Step 8. Create login page
app/login/layout.tsx:
app/login/page.tsx:
The login page uses robots: { index: false, follow: false } to prevent indexing.
Step 9. Create customer operations
Create lib/shopify/types/customer.ts with domain types for Customer, Address, Order, Fulfillment, and related types.
Create lib/shopify/operations/customer.ts with:
discoverCustomerApiEndpoint()— auto-discovers GraphQL endpoint from.well-known/customer-account-apicustomerApiFetch()— GraphQL client with Bearer token authgetCustomer(accessToken)— profile datagetOrders(accessToken, options)— paginated ordersgetOrder(accessToken, orderId)— single order detailgetAddresses(accessToken)— address bookupdateCustomer(accessToken, input)— profile mutationcreateAddress(accessToken, address)— add addressupdateAddress(accessToken, addressId, address)— edit addressdeleteAddress(accessToken, addressId)— remove addresssetDefaultAddress(accessToken, addressId)— set default
Validate field names with shopify-ai-toolkit or vercel-shop:fetch-shopify-schema. All operations use the Customer Account API (separate from Storefront API) with OAuth Bearer tokens.
Step 10. Create account pages and components
Create the account section with this structure:
All account pages must call requireSession() or requireCustomerSession() before rendering. The layout uses getTranslations("account") for i18n.
Step 11. Wire nav account component
Create components/layout/nav/account.tsx — a server component that renders an icon-only account link matching the spartan style of the cart icon:
Then add to components/layout/nav/index.tsx:
Step 12. Wire cart actions for authenticated checkout
In components/cart/actions.ts, import getSession and update:
buyNowAction: RunaddToCartandgetSessionin parallel. If authenticated, calllinkCartToCustomer(session.accessToken)before returning checkout URL.prepareCheckoutAction: Check session, if authenticated calllinkCartToCustomer(session.accessToken), fall back to plain cart checkout URL.
Step 13. Wire chat context for authenticated users
In app/api/chat/route.ts, add a resolveUser function:
Update lib/agent/context.ts to include the authenticated user variant in the User type:
Step 14. Add translation keys
Add to ALL locale files under nav:
signIn,signOut,profile,orders
Add seo.loginTitle.
Add common.loginRedirecting, common.loginNotRedirected, common.loginClickHere.
Add full account, orders, and address sections. See the base en.json translations for the complete key set.