Skip to main content

Shopify Custom App Setup Guide

Purpose: Step-by-step instructions for creating and configuring a Shopify Custom App for the Eli Health mobile app integration.

Date: November 12, 2025 Prerequisites: Shopify Admin access to Eli Health store


Overview​

This guide covers:

  1. Creating a single Custom App in Shopify
  2. Configuring multiple redirect URIs (dev, staging, production)
  3. Setting up API scopes and credentials
  4. Creating test accounts for development
  5. Environment-specific configuration

Important: You only need ONE Custom App that serves all environments (dev, staging, prod) using multiple redirect URIs.


Current Eli App URL Schemes​

Based on your existing app configuration:

EnvironmentiOS URL SchemeBackend API
Develi://https://dev.eli-app.com
Stagingeli://https://staging.eli-app.com
Productioneli://https://app.eli.health

Note: The same custom URL scheme (eli://) is used for all environments. The app determines the environment based on build configuration.


Part 1: Create Custom App in Shopify​

Step 1: Enable Custom App Development​

  1. Log into Shopify Admin: https://[your-store].myshopify.com/admin
  2. Navigate to: Settings β†’ Apps and sales channels
  3. Click "Develop apps" tab
  4. If prompted, click "Allow custom app development"
  5. Confirm the prompt about custom app permissions

Step 2: Create the Custom App​

  1. Click "Create an app"
  2. Fill in app details:
    • App name: Eli Health Mobile
    • App developer: Select your admin email
  3. Click "Create app"

Part 2: Configure API Scopes βœ… COMPLETED​

Step 3: Admin API Configuration βœ…β€‹

You've already completed this! Here's what you configured:

Selected Scopes:

  • βœ… read_products - Read product catalog
  • βœ… read_customers - Read customer information
  • βœ… read_orders - Read order data
  • βœ… read_inventory - Read inventory levels

Admin API Scopes Configuration

Perfect! These scopes give the backend everything it needs to:

  • Sync product catalog
  • Query customer information
  • Retrieve order history
  • Check product availability

No write permissions needed since all product management happens in Shopify Admin.

Step 4: Customer Account API Configuration βœ… COMPLETED​

You've already completed this via the Headless sales channel!

What you did:

  1. Navigated to Sales Channels β†’ Headless
  2. Clicked "Create storefront"
  3. Created storefront: "Eli Health Mobile"
  4. Clicked "Manage" on Customer Account API
  5. Configured as Public (web app) client type (correct for mobile PKCE)
  6. Added callback URL (see Step 5 below)
  7. CRITICAL: Added JavaScript Origins (see Step 5a below)

Credentials obtained:

  • βœ… Client ID: 2d989895-5913-4700-b2be-48f1af8929da
  • βœ… Authorization endpoint: https://account.eli.health/authentication/oauth/authorize
  • βœ… Token endpoint: https://account.eli.health/authentication/oauth/token
  • βœ… No client secret (PKCE public client - correct for mobile)

IMPORTANT: Custom Vanity Domain Eli Health uses a custom vanity domain (account.eli.health) instead of Shopify's default domain. The OAuth endpoints use:

  • Domain: account.eli.health (custom domain configured in Shopify)
  • Path: /authentication/oauth/ (not /oauth/ or /account/oauth/)

How to discover the correct endpoints: You can verify the OAuth URLs by querying Shopify's OpenID discovery endpoint:

curl https://elihealth.myshopify.com/.well-known/openid-configuration | jq -r '.authorization_endpoint, .token_endpoint'

This will return the actual configured endpoints for your store.


Part 3: Configure Redirect URI βœ… COMPLETED​

Step 5: Add Redirect URI βœ…β€‹

You've already configured this!

Callback URL configured:

https://app.eli.health/shopify/callback

Why Universal Link instead of custom URL scheme:

  • βœ… Shopify requires HTTPS callback URLs (rejects eli:// scheme)
  • βœ… More secure (prevents other apps from intercepting)
  • βœ… Modern iOS best practice
  • βœ… Already have Universal Links working for signup/reset-password

Important Format Requirements:

  • βœ… Must be HTTPS
  • βœ… Case-sensitive
  • βœ… No trailing slash
  • βœ… Must match exactly what's configured in Shopify

Saved in Shopify: βœ… Done

How to Add/Edit Callback URLs in Shopify​

Step-by-Step with Screenshots:

  1. Navigate to Sales Channels β†’ Headless

    Step 1: Navigate to Headless

  2. Find your storefront and click "Manage" on Customer Account API

    Step 2: Manage Customer Account API

  3. Add or edit callback URLs

    Step 3: Add Callback URLs

    • Click "Add redirect URI"
    • Enter your HTTPS callback URL
    • You can have multiple callback URLs (production, dev, local testing)
    • Click "Save"

Multiple Callback URLs (Recommended):

You can configure multiple callback URLs for different environments:

https://app.eli.health/shopify/callback           # Production
https://dev.eli-app.com/shopify/callback # Development
https://staging.eli-app.com/shopify/callback # Staging
https://abc123.ngrok-free.app/shopify/callback # Local testing (ngrok)

This allows you to test OAuth flow in all environments without reconfiguring Shopify each time.

How This Works​

When users authenticate via Shopify OAuth:

  1. Shopify redirects to: https://app.eli.health/shopify/callback?code=xxxxx&state=xxxxx
  2. Backend receives request (shopify-callback.controller.ts)
  3. Backend redirects to custom scheme: eli://shopify/callback?code=xxxxx&state=xxxxx
  4. iOS app opens via deep link handler
  5. App processes OAuth callback and exchanges code for token

This leverages your existing Universal Links infrastructure (AASA file already updated).


Step 5a: Configure JavaScript Origins ⚠️ CRITICAL - REQUIRED​

What are JavaScript Origins?

JavaScript Origins are the domains from which your backend API makes GraphQL requests to Shopify's Customer Account API. Shopify requires an Origin header in all GraphQL requests, and that origin must be in the allowed list.

⚠️ Without this configuration, you will get 401 "Invalid code" errors!

How to Configure:

  1. Navigate to Sales Channels β†’ Headless in Shopify Admin
  2. Find your storefront: "Eli Health Mobile"
  3. Click "Manage" on Customer Account API
  4. Scroll to the "JavaScript Origins" section
  5. Click "Add JavaScript origin"
  6. Enter: https://account.eli.health
  7. Click "Save"

Multiple Origins for Different Environments:

You can add multiple origins for dev, staging, and production:

https://account.eli.health              # Production
https://dev.eli-app.com # Development
https://staging.eli-app.com # Staging
https://abc123.ngrok-free.app # Local testing (ngrok)

Why this is required:

The backend includes an Origin header in GraphQL requests:

headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
'Origin': 'https://account.eli.health', // Must match JavaScript Origins
}

Shopify validates this origin against the configured list. If the origin is not in the list, Shopify returns a 401 error.

Reference: Shopify Customer Account API - Origin Header Requirement


Part 4: Install the App and Get Credentials​

Step 6: Install the Custom App​

  1. Go to "API credentials" tab
  2. Click "Install app" button
  3. Confirm installation

Step 7: Collect API Credentials βœ… COMPLETED​

You've collected all necessary credentials!

1. Admin API Access Token βœ…β€‹

Status: βœ… Obtained and stored

Store Domain: elihealth.myshopify.com
Admin API Token: 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
API Key: 2641be696c6c743a75a979445f1f2a1c
API Secret: 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

Usage: Backend queries (products, customers, orders, inventory)

Scopes: read_products, read_customers, read_orders, read_inventory

2. OAuth Client Credentials βœ…β€‹

Status: βœ… Obtained via Headless storefront

Client ID: 2d989895-5913-4700-b2be-48f1af8929da
Client Type: Public (web app) - PKCE flow
Authorization URL: https://account.elihealth.myshopify.com/oauth/authorize
Token URL: https://account.elihealth.myshopify.com/oauth/token
Callback URL: https://app.eli.health/shopify/callback

⚠️ NOTE: No client secret for PKCE (public client for mobile apps)

Usage:

  • Client ID: Used by mobile app to initiate OAuth flow
  • No secret needed: PKCE uses code_verifier/code_challenge instead

Storage:

  • Credentials documented in private file (contact Chip for access)
  • Backend environment variables to be added (see "What's Next" below)

3. Storefront API Access Token (Not Yet Created)​

Status: ⏸️ Optional - can be added later if needed

Usage: Public product browsing, checkout (if needed beyond Admin API)


Part 5: Testing Strategy​

Testing Accounts​

You need test Shopify customer accounts to test OAuth flow. Two options:

  1. Go to Shopify Admin β†’ Customers
  2. Click "Add customer"
  3. Fill in details:
    • Email: test-dev@eli.health (or your email)
    • First name: Test
    • Last name: User Dev
    • Password: Set a password
  4. Click "Save"
  5. Repeat for each environment if needed:
    • test-dev@eli.health
    • test-staging@eli.health
    • test-prod@eli.health

Pros:

  • βœ… Real customer accounts in your Shopify
  • βœ… Can test full checkout flow
  • βœ… Orders appear in your real order list (tagged as test)

Cons:

  • ❌ Test orders appear in analytics (use test_order tag to filter)

Option B: Use Personal Shopify Accounts​

  1. Create Shopify customer accounts using your personal email
  2. Use for testing OAuth and checkout
  3. Delete when done

Pros:

  • βœ… Can test across multiple Shopify stores (if you have access)

Cons:

  • ❌ Uses real email addresses
  • ❌ Test orders mixed with personal orders (if you shop at your store)

Environment-Specific Testing​

EnvironmentWhat to TestTest Account
DevOAuth flow, API integration, UItest-dev@eli.health
StagingFull checkout, order sync, webhookstest-staging@eli.health
ProductionSmoke test only, monitor real usersReal user accounts

Part 6: Verification Checklist​

βœ… Completed​

Admin API​

  • βœ… Admin API access token created
  • βœ… Token has scopes: read_products, read_customers, read_orders, read_inventory
  • βœ… Credentials documented (available from Chip or Eli dev team)

OAuth Configuration​

  • βœ… Headless storefront created: "Eli Health Mobile"
  • βœ… Customer Account API configured
  • βœ… Client ID generated: 2d989895-5913-4700-b2be-48f1af8929da
  • βœ… No client secret (PKCE public client - correct for mobile)
  • βœ… Callback URL configured: https://app.eli.health/shopify/callback
  • βœ… Backend controller created: shopify-callback.controller.ts
  • βœ… AASA file updated with /shopify/callback path
  • βœ… Controller created: eli-backend-api/src/modules/deep-links/shopify-callback.controller.ts
  • βœ… Registered in app.module.ts
  • βœ… Route: GET /shopify/callback
  • βœ… Handles OAuth redirect from Shopify
  • βœ… Passes code to app via eli://shopify/callback?code=...

⏭️ Next Steps (Not Yet Started)​

Backend Deployment​

  • Deploy backend with new shopify-callback controller
  • Test endpoint: https://app.eli.health/shopify/callback?code=test

Backend Environment Variables​

  • Add Shopify credentials to backend .env files
  • Test Admin API connection

Test Accounts​

  • Create test customer account in Shopify
  • Document test credentials

Mobile App Implementation​

  • Implement OAuth PKCE flow
  • Handle eli://shopify/callback deep link
  • Exchange authorization code for access token
  • Store tokens securely

Part 7: Configure Backend Environment Variables​

Add these to your backend .env files (or Google Secret Manager for prod):

# Shopify Configuration
SHOPIFY_STORE_DOMAIN=elihealth.myshopify.com

# Admin API (for backend operations)
SHOPIFY_ADMIN_ACCESS_TOKEN=shpat_xxxxxxxxxxxxxxxxxxxxx

# OAuth 2.0 Customer Account API
SHOPIFY_OAUTH_CLIENT_ID=shp_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
SHOPIFY_OAUTH_CLIENT_SECRET=shpss_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

# Storefront API (optional)
SHOPIFY_STOREFRONT_ACCESS_TOKEN=[if created]

For each environment:

  • Dev backend (local): Create .env.development with these values
  • Staging backend: Add to staging environment config
  • Production backend: Use Google Secret Manager (existing pattern)

Part 8: Mobile App Configuration​

The mobile app needs the Client ID and Store Domain. Two approaches:

Option A: Hardcode in Mobile App (Simpler)​

Add to .env-cmdrc.js:

module.exports = {
dev: {
// ... existing config
REACT_APP_SHOPIFY_CLIENT_ID: 'shp_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
REACT_APP_SHOPIFY_STORE_DOMAIN: 'elihealth.myshopify.com',
},
stg: {
// ... existing config
REACT_APP_SHOPIFY_CLIENT_ID: 'shp_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
REACT_APP_SHOPIFY_STORE_DOMAIN: 'elihealth.myshopify.com',
},
prod: {
// ... existing config
REACT_APP_SHOPIFY_CLIENT_ID: 'shp_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
REACT_APP_SHOPIFY_STORE_DOMAIN: 'elihealth.myshopify.com',
},
};

Note: Client ID is public (not sensitive), so it's safe to include in mobile app.

Option B: Fetch from Backend (More Secure)​

Mobile app calls GET /shopify/oauth/config to get Client ID and store domain from backend. This way credentials stay on server.


Part 9: iOS Configuration Updates​

Add Shopify callback to iOS URL schemes:

Edit Info.plist​

Location: /ios/Eli/Info.plist

Current URL schemes:

<key>CFBundleURLSchemes</key>
<array>
<string>eli</string>
</array>

No changes needed! The existing eli:// scheme already handles eli://shopify/callback.

Shopify OAuth will redirect to: eli://shopify/callback?code=xxxxx&state=xxxxx

Your app's deep linking handler needs to recognize the /shopify/callback path.


Part 10: Testing OAuth Flow (Before Mobile Implementation)​

Before implementing OAuth in the mobile app, test it manually to verify Shopify setup:

Browser Test (Quick Verification)​

  1. Get your OAuth authorization URL:
https://elihealth.myshopify.com/account/authorize?
client_id=YOUR_CLIENT_ID
&scope=openid+email+customer-account-api:full
&redirect_uri=eli://shopify/callback
&state=random_state_string
&response_type=code
  1. Replace:

    • YOUR_CLIENT_ID with your actual Client ID from Shopify
    • random_state_string with any random string (e.g., test123)
  2. Open URL in Safari on your iPhone (must have Eli app installed)

  3. Log in with test customer account

  4. Authorize the app

  5. iOS should prompt: "Open in Eli?" β†’ Tap Open

  6. Your app should open with the OAuth callback

Note: Since eli:// is a custom scheme, you can't test in desktop browser. You must test on actual iOS device with Eli app installed.

Expected result:

  • If setup correct: App opens via deep link with eli://shopify/callback?code=...&state=...
  • If setup incorrect: iOS shows "Cannot Open Page" or Shopify shows error

Testing without implementing deep link handler: Since you haven't implemented the OAuth callback handler yet, the app might open but not do anything. That's OK! The goal is to verify:

  1. Shopify OAuth page loads
  2. You can log in with test account
  3. iOS prompts to open Eli app
  4. App opens (even if it doesn't handle the callback yet)

Common Errors​

ErrorCauseFix
"Redirect URI mismatch"Redirect URI not configuredAdd exact URI to Shopify app config
"Invalid client"Wrong Client IDDouble-check Client ID is correct
"Invalid scope"Scope not grantedEnable Customer Account API scopes

Part 11: Summary & Next Steps​

βœ… What You Now Have (COMPLETED)​

  • βœ… Custom App created: "Eli Health Mobile"
  • βœ… Headless storefront created: "Eli Health Mobile"
  • βœ… Admin API Token: shpat_7dbc...5948 with read-only scopes
  • βœ… OAuth Client ID: 2d989895-5913-4700-b2be-48f1af8929da
  • βœ… PKCE public client configured (no client secret needed)
  • βœ… Callback URL: https://app.eli.health/shopify/callback
  • βœ… Backend deep link handler: shopify-callback.controller.ts
  • βœ… AASA file updated with /shopify/callback path
  • βœ… Store domain: elihealth.myshopify.com
  • βœ… Credentials documented (contact Chip or Eli dev team for access)

πŸš€ What's Next (YOUR IMMEDIATE TASKS)​

1. Deploy Backend Changes ⚑ DO THIS FIRST​

The backend controller is created but not yet deployed. Deploy to enable the OAuth callback:

cd eli-backend-api
npm run build
# Deploy to your environment

Test it works:

https://app.eli.health/shopify/callback?code=test123&state=abc

Should show fallback page with "Shopify Account Linking" heading.

2. Add Backend Environment Variables​

Add to eli-backend-api/.env (or Google Secret Manager for prod):

# Shopify Configuration
SHOPIFY_STORE_DOMAIN=elihealth.myshopify.com

# Admin API (for backend queries)
SHOPIFY_ADMIN_ACCESS_TOKEN=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
SHOPIFY_API_KEY=2641be696c6c743a75a979445f1f2a1c
SHOPIFY_API_SECRET=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

# OAuth Customer Account API (for mobile customer auth)
SHOPIFY_OAUTH_CLIENT_ID=2d989895-5913-4700-b2be-48f1af8929da
SHOPIFY_OAUTH_CALLBACK_URL=https://app.eli.health/shopify/callback
SHOPIFY_OAUTH_AUTHORIZE_URL=https://account.elihealth.myshopify.com/oauth/authorize
SHOPIFY_OAUTH_TOKEN_URL=https://account.elihealth.myshopify.com/oauth/token

3. Create Test Customer Account in Shopify​

  1. Go to Shopify Admin β†’ Customers
  2. Click "Add customer"
  3. Fill in:
    • Email: test-shopify@eli.health
    • First name: Test
    • Last name: Shopify
    • Password: Create a password
  4. Click "Save"
  5. Test login at: https://account.elihealth.myshopify.com

4. Implement Mobile App OAuth Flow (Phase 2)​

This is the main implementation work. You'll need to:

  1. Install dependencies:

    npm install react-native-inappbrowser-reborn
    # For PKCE: generate code_verifier and code_challenge
  2. Create Shopify OAuth service (services/shopify-oauth.ts):

    • Generate PKCE code_verifier and code_challenge
    • Build authorization URL
    • Open in-app browser
    • Handle callback via deep link
    • Exchange code for access token
  3. Update deep link handler (UniversalLinkHandler.tsx):

    • Add route for /shopify/callback
    • Extract code and state parameters
    • Verify state (CSRF protection)
    • Call backend to exchange code for token
  4. Create Shopify API service:

    • Store access token securely (encrypted AsyncStorage)
    • Call Shopify Customer Account API
    • Link Shopify customer ID with Eli user ID
  5. Add UI screen:

    • "Connect Shopify Account" button
    • OAuth flow progress indicators
    • Success/error handling

See: /home/chipdev/eli.health/eli-docs/docs/implementation-plans/shopify-integration.md for detailed mobile implementation plan.


Troubleshooting​

Issue: "Redirect URI mismatch"​

Cause: The redirect URI in the OAuth request doesn't match what's configured in Shopify.

Solution:

  1. Check Shopify app configuration: Settings β†’ Apps β†’ Eli Health Mobile App β†’ Configuration β†’ Customer Account API β†’ Redirect URIs
  2. Verify exact match (including http/https, trailing slash, path)
  3. Remember: iOS uses eli://shopify/callback, Android uses https://...

Issue: "Invalid client"​

Cause: Client ID is incorrect or app not installed.

Solution:

  1. Go to Shopify Admin β†’ Apps β†’ Eli Health Mobile
  2. Verify app is installed (should show "Installed" status)
  3. Double-check Client ID matches

Issue: iOS doesn't prompt to open app​

Cause: Custom URL scheme not registered or app not installed.

Solution:

  1. Verify app is installed on device
  2. Check Info.plist has eli URL scheme registered
  3. Try uninstalling and reinstalling app
  4. Test with simple URL: eli://test in Safari to verify scheme works

Issue: OAuth works in dev but not prod​

Cause: Different redirect URI or environment mismatch.

Solution:

  1. Verify correct redirect URI used for prod build
  2. Check prod backend uses correct Client ID/Secret
  3. Verify prod domain universal links configured

Part 12: Local Testing with Ngrok​

For local development and testing, you need HTTPS because Shopify requires secure callback URLs. Using http://localhost won't work. Here's how to set up local testing with ngrok.

Why Ngrok?​

Problem: Shopify requires HTTPS callback URLs, but your local backend runs on http://localhost:7429

Solution: Ngrok creates a secure HTTPS tunnel to your local backend

Alternatives considered:

  • ❌ http://localhost:7429 - Shopify rejects HTTP URLs
  • ❌ localtest.me with mkcert - Requires SSL certificate setup and mobile device certificate trust
  • βœ… Ngrok - Instant HTTPS, no certificate management needed

Setup Instructions​

1. Install Ngrok​

Ngrok is likely already installed. Check with:

which ngrok
ngrok version

If not installed:

# On Ubuntu/Debian
curl -s https://ngrok-agent.s3.amazonaws.com/ngrok.asc | sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null
echo "deb https://ngrok-agent.s3.amazonaws.com buster main" | sudo tee /etc/apt/sources.list.d/ngrok.list
sudo apt update
sudo apt install ngrok

# On macOS
brew install ngrok

2. Get Ngrok Auth Token​

  1. Go to: https://dashboard.ngrok.com/get-started/your-authtoken
  2. Sign up or log in (free account works)
  3. Copy your authtoken

3. Configure Ngrok​

ngrok config add-authtoken YOUR_AUTH_TOKEN_HERE

This saves your token to ~/.config/ngrok/ngrok.yml

4. Start Ngrok Tunnel​

With your backend running on port 7429:

ngrok http 7429

You'll see output like:

Session Status                online
Account Your Name (Plan: Free)
Version 3.x.x
Region United States (us)
Latency -
Web Interface http://127.0.0.1:4040
Forwarding https://3ceb60d5132b.ngrok-free.app -> http://localhost:7429

Connections ttl opn rt1 rt5 p50 p90
0 0 0.00 0.00 0.00 0.00

Copy the HTTPS URL: https://3ceb60d5132b.ngrok-free.app

Note: This URL changes every time you restart ngrok (unless you pay for a static domain)

5. Update Application Configuration​

Backend .env file:

# Update callback URL to use ngrok
SHOPIFY_OAUTH_CALLBACK_URL=https://3ceb60d5132b.ngrok-free.app/shopify/callback

Mobile app shopify-oauth.ts:

const SHOPIFY_CALLBACK_URL = 'https://3ceb60d5132b.ngrok-free.app/shopify/callback';

Mobile app .env.local:

REACT_APP_API_ENDPOINT=https://3ceb60d5132b.ngrok-free.app

6. Add Ngrok URL to Shopify​

  1. Go to Shopify Admin: https://admin.shopify.com/store/elihealth
  2. Navigate to: Sales channels β†’ Headless
  3. Find: "Eli Health Mobile" storefront
  4. Click: "Manage" on Customer Account API
  5. Add callback URL: https://3ceb60d5132b.ngrok-free.app/shopify/callback
  6. Keep existing production URL: https://app.eli.health/shopify/callback
  7. Save changes

You can have multiple callback URLs, so you don't need to remove the production one.

Testing the Flow​

  1. Start backend locally: docker compose --profile backend up
  2. Start ngrok: ngrok http 7429 (in separate terminal)
  3. Note ngrok URL and update config files
  4. Add ngrok URL to Shopify callback URLs
  5. Start mobile app in local/dev mode
  6. Test OAuth flow:
    • Open Settings β†’ Shopify Store β†’ Connect Shopify Account
    • Login with Shopify customer account
    • Authorize the app
    • Should redirect back to app

Ngrok Web Interface​

Ngrok provides a web interface at http://localhost:4040 where you can:

  • See all HTTP requests/responses
  • Inspect OAuth callback requests
  • Debug authentication flow
  • Replay requests

Very useful for debugging!

Tips​

Keep ngrok running: Don't close the ngrok terminal - if you do, the URL stops working

URL changes: Free ngrok URLs change on restart. Update config files each time you restart ngrok.

Static URLs: Ngrok paid plans offer static domains like eli-backend.ngrok.app that don't change

Multiple tunnels: You can run multiple ngrok tunnels if you need to expose multiple local services

Cleanup After Testing​

When you're done with local testing:

  1. Stop ngrok: Press Ctrl+C in the ngrok terminal
  2. Revert config changes:
    • Backend .env: Change callback URL back to production
    • Mobile app: Revert to production API endpoint
  3. Optional: Remove ngrok callback URL from Shopify (or leave it for next time)

Common Issues​

Issue: "ERR_NGROK_107: Authentication failed"

Solution: Your authtoken is invalid or expired

Issue: Ngrok URL changes and breaks the flow

Solution:

  • Get the new ngrok URL from terminal output
  • Update all config files with the new URL
  • Update Shopify callback URL

Issue: "This site can't be reached"

Solution:

  • Make sure ngrok is running
  • Make sure backend is running on port 7429
  • Check ngrok is tunneling to the correct port

Troubleshooting​

Issue: "Invalid code" error during token exchange​

Symptom: Mobile app successfully authenticates with Shopify but fails with "Invalid code" error when exchanging the authorization code for an access token.

Root Cause: OAuth authorization codes are single-use only. The in-app browser was caching the callback URL with an already-used authorization code, causing the same code to be reused on subsequent connection attempts.

Solution: Enable ephemeral web sessions in the in-app browser to ensure fresh PKCE parameters and authorization codes on each attempt.

// In shopify-oauth.ts
const result = await InAppBrowser.openAuth(authUrl, SHOPIFY_CALLBACK_URL, {
ephemeralWebSession: true, // IMPORTANT: Use ephemeral session to ensure fresh OAuth flow each time
showTitle: false,
enableUrlBarHiding: true,
enableDefaultShare: false,
});

Issue: SSL certificate errors when accessing OAuth endpoints​

Symptom: Mobile app shows "This connection is not private" SSL certificate error when trying to authenticate.

Root Cause: Incorrect OAuth endpoint URLs. The custom vanity domain must use the correct path format.

Solution: Verify you're using the correct domain and path:

  • βœ… Correct: https://account.eli.health/authentication/oauth/authorize
  • ❌ Wrong: https://account.elihealth.myshopify.com/oauth/authorize
  • ❌ Wrong: https://account.eli.health/oauth/authorize (missing /authentication)
  • ❌ Wrong: https://elihealth.myshopify.com/account/oauth/authorize

How to verify: Use Shopify's OpenID discovery endpoint:

curl https://elihealth.myshopify.com/.well-known/openid-configuration | jq -r '.authorization_endpoint, .token_endpoint'

Issue: Stuck at "signing you in" on Shopify page​

Symptom: User authenticates successfully on Shopify but gets stuck on a loading screen.

Possible Causes:

  1. Incorrect OAuth token URL in backend environment variables
  2. Backend not properly configured to handle the callback
  3. Network connectivity issues between backend and Shopify

Solution:

  1. Verify backend environment variable SHOPIFY_OAUTH_TOKEN_URL is set to https://account.eli.health/authentication/oauth/token
  2. Check GCP Cloud Run logs for any errors during token exchange
  3. Ensure backend service account has proper permissions

Issue: Backend failing to exchange authorization code​

Symptom: Backend returns 401 Unauthorized when calling Shopify token endpoint.

Root Cause: Missing or incorrect code_verifier in token exchange request. PKCE requires the mobile app's code_verifier to be sent to the backend for token exchange.

Solution: Ensure the mobile app sends the code_verifier along with the code and state to the backend's /shopify/oauth/exchange endpoint.


Issue: 401 "Invalid code" error when querying Customer Account GraphQL API​

Symptom: Token exchange succeeds, but GraphQL queries return HTTP 401 with error message "Invalid code" or "invalid_token".

Root Cause: Missing Origin header or Origin not configured in Shopify JavaScript Origins list.

Solution:

  1. Verify Backend Includes Origin Header: Check that your backend code includes the Origin header in GraphQL requests:

    headers: {
    'Authorization': `Bearer ${accessToken}`,
    'Content-Type': 'application/json',
    'Origin': 'https://account.eli.health', // REQUIRED!
    }
  2. Add Origin to Shopify JavaScript Origins:

    • Go to Shopify Admin β†’ Sales Channels β†’ Headless
    • Find your storefront ("Eli Health Mobile")
    • Click "Manage" on Customer Account API
    • Scroll to "JavaScript Origins" section
    • Add: https://account.eli.health
    • Click "Save"
  3. Verify the Origin Matches: The Origin header in your code must EXACTLY match one of the origins configured in Shopify. Even http vs https or a trailing slash will cause a mismatch.

Why this is required: Shopify's Customer Account API requires an Origin header for CORS security. Without this header, or if the origin isn't in the allowed list, Shopify rejects the request with a 401 error.

Reference: See "Step 5a: Configure JavaScript Origins" for detailed setup instructions.


References​


Change Log​

DateAuthorChanges
2025-11-12SamanthaInitial setup guide created
2025-11-14SamanthaAdded callback URL management screenshots and ngrok local testing guide
2025-11-14ClaudeCorrected OAuth URLs to use custom vanity domain account.eli.health/authentication/oauth/, added troubleshooting section with common issues and solutions