Skip to Content
OperationsAuth0 Configuration

Auth0 Configuration

Tenant

PropertyValue
Tenantgatelithix
Domaingatelithix.us.auth0.com
RegionUS
Dashboardhttps://manage.auth0.com/dashboard/us/gatelithix 

Applications

Gatelithix Dashboard (SPA)

PropertyValue
Client IDI1rEXZhT4pRcHIBYXoUG3x0J2NgVuZKq
TypeSingle Page Application
Auth methodNone (PKCE)
Callback URLshttps://dashboard.paylithix.com
Logout URLshttps://dashboard.paylithix.com
Web Originshttps://dashboard.paylithix.com

API Explorer Application (M2M)

PropertyValue
Client IDSszFMlZa9HIhRNadnXI6deFzei3ybnGj
TypeMachine to Machine
PurposeAuth0 Management API access, gateway server-side auth
Client SecretStored in GCP Secret Manager: auth0-client-secret (version 3)

APIs (Resource Servers)

Gatelithix Gateway API

PropertyValue
Identifierhttps://api.gatelithix.com
Signing AlgorithmRS256
Token Lifetime86400 seconds (24 hours)
Scopesread:payments, write:payments, admin

How Auth Works

Dashboard Login Flow

  1. User visits https://dashboard.paylithix.com
  2. Next.js app redirects to https://gatelithix.us.auth0.com/authorize with SPA client ID
  3. User authenticates (email/password via Auth0 Universal Login)
  4. Auth0 redirects back to /auth/callback with authorization code
  5. Admin app exchanges code for tokens (PKCE flow)
  6. JWT access token includes aud: https://api.gatelithix.com
  7. Admin app includes token in API calls to gateway

Gateway JWT Validation

  1. Gateway reads AUTH0_DOMAIN env var (gatelithix.us.auth0.com)
  2. Fetches JWKS from https://gatelithix.us.auth0.com/.well-known/jwks.json
  3. Validates JWT signature, audience (https://api.gatelithix.com), and expiry
  4. Extracts user identity from token claims

Environment Variables

Gateway (Cloud Run)

VariableValueSource
AUTH0_DOMAINgatelithix.us.auth0.comTerraform env_vars
AUTH0_AUDIENCEhttps://api.gatelithix.comTerraform env_vars
AUTH0_CLIENT_SECRET_NAMEprojects/gatelithix-core/secrets/auth0-client-secret/versions/latestTerraform env_vars

Dashboard (Build-time)

VariableValueSource
NEXT_PUBLIC_AUTH0_DOMAINgatelithix.us.auth0.comGitHub Actions vars / Docker build arg
NEXT_PUBLIC_AUTH0_CLIENT_IDI1rEXZhT4pRcHIBYXoUG3x0J2NgVuZKqGitHub Actions vars / Docker build arg
NEXT_PUBLIC_AUTH0_AUDIENCEhttps://api.gatelithix.comGitHub Actions vars / Docker build arg
NEXT_PUBLIC_API_URLhttps://api.gatelithix.comGitHub Actions vars / Docker build arg

GCP Secret Manager

SecretPurposeCurrent Version
auth0-client-secretM2M client secret for gateway server-side authv3

Post-Login Action: Add Roles to Tokens

A post-login Action reads app_metadata.roles and injects them into the JWT as a custom claim (https://gatelithix.com/roles). Without this Action, the dashboard navigation is empty because all nav items are gated by RoleGate.

Action name: Add roles to tokens Trigger: post-login (v3) Code:

exports.onExecutePostLogin = async (event, api) => { const roles = event.user.app_metadata?.roles || []; if (roles.length > 0) { api.idToken.setCustomClaim("https://gatelithix.com/roles", roles); api.accessToken.setCustomClaim("https://gatelithix.com/roles", roles); } };

Assigning Roles to Users

Roles are stored in app_metadata.roles. To assign a role:

TOKEN=$(curl -s -X POST "https://gatelithix.us.auth0.com/oauth/token" \ -H "Content-Type: application/json" \ -d '{"client_id":"SszFMlZa9HIhRNadnXI6deFzei3ybnGj","client_secret":"'$(gcloud secrets versions access latest --secret=auth0-client-secret --project=gatelithix-core)'","audience":"https://gatelithix.us.auth0.com/api/v2/","grant_type":"client_credentials"}' | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])") # Find user by email curl -s "https://gatelithix.us.auth0.com/api/v2/users?q=email:USER@EXAMPLE.COM" \ -H "Authorization: Bearer $TOKEN" | python3 -c "import sys,json; [print(u['user_id']) for u in json.load(sys.stdin)]" # Assign role (URL-encode the user_id: | → %7C) curl -s -X PATCH "https://gatelithix.us.auth0.com/api/v2/users/USER_ID_HERE" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"app_metadata":{"roles":["platform_admin"]}}'

Available roles: platform_admin, platform_support, iso_admin, merchant_admin, merchant_developer, read_only_auditor

Local Development

The production Auth0 app does NOT include http://localhost URLs — adding localhost to a production OAuth app is a security risk (redirect attack vector).

For local dev, use DEV_MODE=true (make dev) which bypasses Auth0 entirely. If you need to test the real Auth0 flow locally, create a separate Auth0 application for development or temporarily add localhost via the Management API and remove it when done.

Debugging

Admin portal returns 403 after Auth0 login

Cloud Armor WAF’s SQL injection rule (sqli-v33-stable) triggers a false positive on the Auth0 OAuth callback URL because the code= and state= query parameters contain base64 strings that resemble SQL injection payloads.

The frontend-waf policy has a priority-900 allow rule that exempts OAuth callbacks (request.path == '/' && request.query.matches('code=.*&state=.*')). If this rule is missing, re-add it:

gcloud compute security-policies rules create 900 \ --security-policy=frontend-waf \ --project=gatelithix-core \ --action=allow \ --expression="request.path == '/' && request.query.matches('code=.*&state=.*')" \ --description="Allow Auth0 OAuth callback (code+state params trigger false positive on sqli-v33)"

Admin portal shows “Callback URL mismatch”

Auth0 is rejecting the redirect_uri. The admin portal sends window.location.origin (e.g., https://dashboard.paylithix.com) as the redirect_uri. This bare origin MUST be in the Allowed Callback URLs list.

# Check current callback URLs via Management API TOKEN=$(curl -s -X POST "https://gatelithix.us.auth0.com/oauth/token" \ -H "Content-Type: application/json" \ -d '{"client_id":"SszFMlZa9HIhRNadnXI6deFzei3ybnGj","client_secret":"'$(gcloud secrets versions access latest --secret=auth0-client-secret --project=gatelithix-core)'","audience":"https://gatelithix.us.auth0.com/api/v2/","grant_type":"client_credentials"}' | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])") # View current callbacks curl -s "https://gatelithix.us.auth0.com/api/v2/clients/I1rEXZhT4pRcHIBYXoUG3x0J2NgVuZKq?fields=callbacks" \ -H "Authorization: Bearer $TOKEN" | python3 -m json.tool # Fix: set callbacks to match what the app sends (window.location.origin) # Production only — no localhost (OAuth redirect attack vector) curl -s -X PATCH "https://gatelithix.us.auth0.com/api/v2/clients/I1rEXZhT4pRcHIBYXoUG3x0J2NgVuZKq" \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"callbacks":["https://dashboard.paylithix.com"],"allowed_logout_urls":["https://dashboard.paylithix.com"],"web_origins":["https://dashboard.paylithix.com"]}'

Admin portal shows “authorize” DNS error

The admin portal is trying to redirect to Auth0 but the domain or client ID is wrong.

# Check what the admin image was built with curl -s https://dashboard.paylithix.com/ | grep -o 'auth0[^"]*' | head -5 # Check GitHub Actions vars gh variable list --repo paylithix/paylithix-gateway | grep AUTH0

Gateway rejects JWT tokens

# Verify JWKS endpoint is reachable curl -s https://gatelithix.us.auth0.com/.well-known/jwks.json | jq '.keys | length' # Check gateway Auth0 config gcloud run services describe api-gateway --project=gatelithix-core --region=us-central1 \ --format="value(spec.template.spec.containers[0].env)" | tr ';' '\n' | grep AUTH0 # Verify Secret Manager has real secret (not placeholder) gcloud secrets versions access latest --secret=auth0-client-secret --project=gatelithix-core | head -c 10

Auth0 Management API access

# Get a fresh Management API token (24-hour expiry) # Go to Auth0 Dashboard → APIs → Auth0 Management API → API Explorer tab → Copy token # Or use M2M credentials: curl -s -X POST "https://gatelithix.us.auth0.com/oauth/token" \ -H "Content-Type: application/json" \ --data '{ "client_id": "SszFMlZa9HIhRNadnXI6deFzei3ybnGj", "client_secret": "'$(gcloud secrets versions access latest --secret=auth0-client-secret --project=gatelithix-core)'", "audience": "https://gatelithix.us.auth0.com/api/v2/", "grant_type": "client_credentials" }' | jq -r '.access_token' # List applications curl -s "https://gatelithix.us.auth0.com/api/v2/clients" \ -H "Authorization: Bearer $TOKEN" | jq '.[].name' # List users curl -s "https://gatelithix.us.auth0.com/api/v2/users" \ -H "Authorization: Bearer $TOKEN" | jq '.[].email'