EQTY LabDocs

Auth0

Configure Auth0 for deployment

View as Markdown

Overview

The Guardian Platform integrates with Auth0 for user authentication and authorization.

Required Auth0 applications:

  • Platform API (M2M) – Machine-to-machine app for backend API token validation and the Governance Service’s startup organization sync
  • Auth Service Backend (M2M) – Machine-to-machine app for user and organization management via the Auth0 Management API
  • Governance Worker (M2M) – Machine-to-machine app for the platform’s automated governance workflows
  • Guardian (SPA) – Single Page Application for frontend authentication

The platform also needs an Auth0 Organization for its users and two Auth0 Actions that add the platform’s claims to user and worker tokens.

The deployment repository’s auth0-bootstrap Helm chart automates most of this: it runs a one-time Kubernetes Job that creates the applications, API, grants, initial admin user, and Actions via the Auth0 Management API. Run it before deploying the platform chart - the services need valid client credentials at startup.

This guide covers:

  • Creating Auth0 applications (bootstrap chart, Auth0 CLI, or Dashboard)
  • Configuring API permissions and scopes
  • Setting up organization support for multi-tenancy
  • Deploying the Auth0 Actions
  • Configuring the Helm chart for Auth0

Prerequisites

  • Auth0 tenant (sign up at auth0.com if needed)
  • Auth0 tenant domain (e.g. your-tenant.us.auth0.com)
  • kubectl and helm access to the deployment namespace
  • jq 1.6 or later, to read the bootstrap’s generated credentials
  • A clone of the deployment repository for the auth0-bootstrap chart and the Auth0 Action sources
  • Auth0 CLI installed (for the manual setup)

Helm Configuration

Add the following to the values.yaml and secrets.yaml files. The quick start’s last step says where each placeholder comes from; the manual setup fills them in step by step.

values.yaml:

yaml
auth-service:
  config:
    idp:
      provider: "auth0"
      issuer: "https://<auth0-domain>/"
      auth0:
        domain: "<auth0-domain>"
        managementAudience: "https://<auth0-domain>/api/v2/"
        apiIdentifier: "https://<domain>"

governance-service:
  config:
    authProvider: "auth0"
    auth0Domain: "<auth0-domain>"

guardian:
  config:
    authProvider: "auth0"
    auth0Domain: "<auth0-domain>"
    auth0ClientId: "<spa-client-id>"
    auth0Audience: "https://<domain>"

# Post-install hook: seeds the governance database with the organization
# and the platform admin
auth0:
  createOrganization: true
  organizationName: "guardian" # Must match the Auth0 Organization name
  displayName: "Guardian Platform"
  createPlatformAdmin: true
  platformAdminEmail: "<admin-email>"
  domain: "<auth0-domain>"

secrets.yaml:

yaml
global:
  secrets:
    create: true
    auth:
      provider: "auth0"
      auth0:
        secretName: "platform-auth0"
        values:
          clientId: "<platform-api-client-id>"
          clientSecret: "<platform-api-client-secret>"
          mgmtClientId: "<auth-service-client-id>"
          mgmtClientSecret: "<auth-service-client-secret>"
    authService:
      secretName: "platform-auth-service"
      values:
        apiSecret: "<auth-service-api-secret>" # Must equal the post-login Action's AUTH_SERVICE_API_SECRET
        jwtSecret: "<jwt-secret>" # Generate with: openssl rand -base64 32
    governanceWorker:
      secretName: "platform-governance-worker"
      values:
        encryptionKey: "<worker-encryption-key>" # Generate with: openssl rand -base64 32
        clientId: "<worker-client-id>"
        clientSecret: "<worker-client-secret>"

Create a Management API application for the bootstrap, run the auth0-bootstrap chart, add the Auth0 Organization, and copy the generated credentials into the platform’s chart values, in seven steps.

1. Create the Bootstrap M2M Application

The bootstrap job calls the Auth0 Management API with its own Machine-to-Machine application. In the Auth0 Dashboard:

  1. Go to Applications > Applications > Create Application, choose Machine to Machine Applications, and name it Governance Bootstrap M2M
  2. Authorize it for the Auth0 Management API with these scopes:
    • read:clients, create:clients, update:clients
    • read:resource_servers, create:resource_servers, update:resource_servers
    • read:client_grants, create:client_grants, update:client_grants
    • read:users, create:users, update:users
    • read:actions, create:actions, update:actions, delete:actions
    • update:client_keys
  3. Copy the Client ID and Client Secret

The credentials-exchange Action does not use this application. The bootstrap creates a separate Guardian Platform Actions application for it, with only the read:users and read:users_app_metadata scopes, and rotates that application’s secret with update:client_keys when the saved one is missing or a token request with it fails for any reason, including an Auth0 outage. The Action keeps the old secret until the run updates it, so if a re-run fails after rotating, re-run until it completes. Keep the bootstrap application to re-run the bootstrap later.

2. Create Pre-Bootstrap Secrets

bash
# Bootstrap M2M credentials, plus the Auth Service API secret the post-login Action sends to Auth Service
kubectl create secret generic auth0-management \
  --from-literal=client-id=<bootstrap-client-id> \
  --from-literal=client-secret=<bootstrap-client-secret> \
  --from-literal=auth-service-api-secret=<auth-service-api-secret> \
  --namespace guardian

# Guardian admin user password
kubectl create secret generic platform-admin \
  --from-literal=password="$(openssl rand -base64 16)" \
  --namespace guardian

<auth-service-api-secret> must equal global.secrets.authService.values.apiSecret in the platform’s secrets.yaml. If guardctl generated that file, use its value; otherwise generate one with openssl rand -base64 32 and use it in both places. The bootstrap and the helper script stop before changing Auth0 when this key is missing while the post-login Action is enabled.

The bootstrap copies auth-service-api-secret into the post-login Action on each run, so after changing it, re-run the bootstrap.

3. Prepare the Bootstrap Values

From the deployment repository checkout, start from the example values file:

bash
cp charts/auth0-bootstrap/examples/values.yaml bootstrap-values.yaml

Edit bootstrap-values.yaml, replacing the CHANGE_ME placeholders. <domain> is the Guardian Platform domain:

yaml
auth0:
  domain: "<auth0-domain>" # No https:// or trailing slash
  api:
    identifier: "https://<domain>"

applications:
  frontend:
    callbacks:
      - "https://<domain>"
      - "http://localhost:5173"
    logoutUrls:
      - "https://<domain>"
      - "http://localhost:5173"
    webOrigins:
      - "https://<domain>"
      - "http://localhost:5173"

actions:
  postLogin:
    authService:
      url: "https://<domain>/authService"

users:
  admin:
    email: "<admin-email>"

The post-login Action sends the Auth Service API secret to Auth Service at actions.postLogin.authService.url. The bootstrap sets no environment metadata on the frontend application, so the Action treats it as dev: a non-empty urlDev takes precedence over url, and urlStaging and urlProduction are never used. Leave urlDev empty unless every sign-in should call that Auth Service. The chart fails to render when both url and urlDev are empty. A bootstrap file from guardctl sets only url.

4. Run the Bootstrap

The job mounts the Action sources from the auth0-actions-source ConfigMap:

bash
kubectl create configmap auth0-actions-source \
  --from-file=scripts/auth0/actions/ \
  --namespace guardian \
  --dry-run=client -o yaml | kubectl apply -f -

helm upgrade --install auth0-bootstrap ./charts/auth0-bootstrap \
  --namespace guardian \
  --values bootstrap-values.yaml \
  --wait \
  --timeout 10m

Instead of these commands, the deployment repository’s helper script checks the secrets named in the values file and the post-login Action’s URL and API secret, refreshes the ConfigMap, runs the chart, and prints the job logs: ./scripts/auth0/bootstrap-auth0.sh -f bootstrap-values.yaml -n guardian. It deletes the job afterwards and prints the command that reads the generated credentials.

helm waits for the job to finish. From a second terminal, follow its logs:

bash
kubectl logs job/auth0-bootstrap-auth0-bootstrap --namespace guardian -f

The job is idempotent - re-running the helm upgrade or the script sets the frontend application’s callbacks, logout URLs, web origins, and grant types to the values, skips the other applications and users that already exist, and updates the API’s scopes, the grants, and the Actions. Keep the frontend URLs in bootstrap-values.yaml: a re-run replaces any added in the Auth0 Dashboard.

5. Save the Generated Credentials

The job saves the client IDs and new client secrets to the auth0-bootstrap-credentials secret (<release>-credentials, or credentialsSecret.name), not to its logs. Read them with jq:

bash
kubectl get secret auth0-bootstrap-credentials --namespace guardian -o json \
  | jq '.data | map_values(@base64d)'
KeyValue
frontend-client-idGuardian Platform Frontend client ID
backend-client-id, backend-client-secretGuardian Platform Backend credentials
worker-client-id, worker-client-secretGovernance Worker credentials
actions-client-id, actions-client-secretGuardian Platform Actions credentials, kept so later runs reuse them
auth0-domainThe tenant domain

Helm creates the secret empty and keeps it on uninstall, and the job can only read and update it. Auth0 returns a client secret only when the application is created, so an application that already existed has only its client ID saved. If a secret is missing, copy or rotate it in the Auth0 Dashboard.

6. Create the Organization

The bootstrap does not create the Auth0 Organization that Studio sign-in requires. Create it before deploying the platform: the Governance Service links its database organization to the Auth0 Organization only when it creates it at startup. In the Auth0 Dashboard:

  1. Under Organizations, create an organization named guardian (the auth0.organizationName value)
  2. On its Members tab, add the admin user the bootstrap created
  3. On its Connections tab, enable Username-Password-Authentication
  4. Under Applications > Guardian Platform Frontend > Login Experience, set the type of users to Business Users and the login flow to Prompt for Organization

7. Update Chart Values

Fill in the Helm Configuration sections:

  • <auth0-domain>: auth0.domain from bootstrap-values.yaml
  • https://<domain>: auth0.api.identifier
  • <spa-client-id>: frontend-client-id
  • platform-auth0 values: backend-client-id and backend-client-secret, for both the clientId/clientSecret and mgmtClientId/mgmtClientSecret pairs
  • governanceWorker values: worker-client-id and worker-client-secret
  • <worker-encryption-key> and <jwt-secret>: generate each with openssl rand -base64 32
  • <auth-service-api-secret>: the auth-service-api-secret from step 2
  • <admin-email>: users.admin.email

Read the auth-service-api-secret back with:

bash
kubectl get secret auth0-management --namespace guardian \
  -o jsonpath='{.data.auth-service-api-secret}' | base64 -d

Then proceed to the Helm Deployment guide. At startup the Governance Service copies the Auth0 Organization into the governance database, and the platform chart’s post-install hook makes the admin its owner. If the governance organization already existed, the sync leaves its idp_org_id empty; set it to the Auth0 Organization ID (org_...) in the organization table, since restarting the service does not fill it.

Alternative: Manual Setup via Auth0 CLI

Create the platform’s Auth0 applications, API, organization, and Actions with the Auth0 CLI, in nine steps.

1. Install and Authenticate Auth0 CLI

Install the Auth0 CLI:

bash
# macOS
brew install auth0/auth0-cli/auth0

# Linux
curl -sSfL https://raw.githubusercontent.com/auth0/auth0-cli/main/install.sh | sh

Authenticate with the Auth0 tenant:

bash
auth0 login

Note the tenant domain shown after login (e.g. your-tenant.us.auth0.com).

Update values.yaml with the tenant domain.

values.yaml -> auth-service.config.idp:

yaml
issuer: "https://<auth0-domain>/"
auth0:
  domain: "<auth0-domain>"
  managementAudience: "https://<auth0-domain>/api/v2/"

values.yaml -> governance-service.config:

yaml
auth0Domain: "<auth0-domain>"

values.yaml -> guardian.config:

yaml
auth0Domain: "<auth0-domain>"

values.yaml -> auth0:

yaml
domain: "<auth0-domain>"

2. Create Platform API (M2M Application)

bash
auth0 apps create \
  --name "EQTY Platform API" \
  --type m2m \
  --description "Backend API for Guardian Platform token validation" \
  --reveal-secrets

The output will look like:

console
=== your-tenant.us.auth0.com application created

 CLIENT ID      abc123def456
 NAME           EQTY Platform API
 TYPE           Machine to Machine
 CLIENT SECRET  your-client-secret-here

Save both the Client ID and Client Secret.

The Governance Service uses these credentials to read Auth0 Organizations at startup, and fails to start without that access. Grant the Management API permission. Replace <platform-api-client-id> with the Client ID from above:

bash
auth0 api post "client-grants" \
  --data '{
    "client_id": "<platform-api-client-id>",
    "audience": "https://<auth0-domain>/api/v2/",
    "scope": ["read:organizations"]
  }'

Update secrets.yaml with the Platform API credentials.

secrets.yaml -> global.secrets.auth.auth0.values:

yaml
clientId: "<platform-api-client-id>"
clientSecret: "<platform-api-client-secret>"

3. Create Auth Service Backend (M2M Application)

bash
auth0 apps create \
  --name "EQTY Auth Service Backend" \
  --type m2m \
  --description "User management for Guardian Platform" \
  --reveal-secrets

Save both the Client ID and Client Secret.

Grant the required Management API permissions. Replace <auth-service-client-id> with the Client ID from above:

bash
auth0 api post "client-grants" \
  --data '{
    "client_id": "<auth-service-client-id>",
    "audience": "https://<auth0-domain>/api/v2/",
    "scope": [
      "read:users", "create:users", "update:users", "delete:users", "create:user_tickets",
      "read:organizations", "create:organizations", "update:organizations",
      "read:organization_members", "create:organization_members", "delete:organization_members",
      "update:clients"
    ]
  }'

Update secrets.yaml with the Auth Service Backend credentials.

secrets.yaml -> global.secrets.auth.auth0.values:

yaml
mgmtClientId: "<auth-service-client-id>"
mgmtClientSecret: "<auth-service-client-secret>"

4. Create Guardian SPA

Replace <domain> with the Guardian Platform domain (e.g. guardian.example.com). Guardian returns to its own origin after sign-in, and http://localhost:5173 is the local development server:

bash
auth0 apps create \
  --name "Guardian Platform Frontend" \
  --type spa \
  --description "Frontend SPA for Guardian Platform" \
  --callbacks "https://<domain>,http://localhost:5173" \
  --logout-urls "https://<domain>,http://localhost:5173" \
  --web-origins "https://<domain>,http://localhost:5173" \
  --reveal-secrets

Save the Client ID from the output.

Update values.yaml with the SPA client ID.

values.yaml -> guardian.config:

yaml
auth0ClientId: "<spa-client-id>"

5. Configure for Business Users (Organizations)

Enable organizations on the SPA to support multi-tenancy. Replace <spa-client-id> with the Client ID from step 4:

bash
auth0 apps update <spa-client-id> \
  --organization-usage require \
  --organization-require-behavior "pre_login_prompt"

6. Create an API Definition

Replace <domain> with the Guardian Platform domain:

bash
auth0 apis create \
  --name "Guardian Platform API" \
  --identifier "https://<domain>" \
  --scopes "governance:declarations:create,integrity:statements:create"

Update values.yaml with the API identifier. Guardian requests tokens for this API, and the Auth Service accepts them.

values.yaml -> auth-service.config.idp.auth0:

yaml
apiIdentifier: "https://<domain>"

values.yaml -> guardian.config:

yaml
auth0Audience: "https://<domain>"

7. Create Governance Worker (M2M Application)

The Governance Worker is the platform’s service account for automated governance workflows.

bash
auth0 apps create \
  --name "EQTY Governance Worker" \
  --type m2m \
  --description "Service account for Guardian Platform workflows" \
  --reveal-secrets

Save both the Client ID and Client Secret.

Authorize it on the API from step 6. Replace <worker-client-id> with the Client ID from above:

bash
auth0 api post "client-grants" \
  --data '{
    "client_id": "<worker-client-id>",
    "audience": "https://<domain>",
    "scope": ["governance:declarations:create", "integrity:statements:create"]
  }'

Generate an encryption key for the worker:

bash
openssl rand -base64 32

Update secrets.yaml with the worker credentials.

secrets.yaml -> global.secrets.governanceWorker.values:

yaml
encryptionKey: "<generated-key>"
clientId: "<worker-client-id>"
clientSecret: "<worker-client-secret>"

8. Create the Organization and Platform Admin

Guardian sign-in requires an Auth0 Organization. Create one with the name set in auth0.organizationName:

bash
auth0 api post "organizations" \
  --data '{"name": "guardian", "display_name": "Guardian Platform"}'

Save the id from the output (it starts with org_).

The platform admin must be a user in the tenant. Use an existing user, or create one in the Auth0 Dashboard under User Management > Users, and note its user ID (e.g. auth0|...). Add the user to the organization:

bash
auth0 api post "organizations/<org-id>/members" \
  --data '{"members": ["<admin-user-id>"]}'

In the Auth0 Dashboard, on the organization’s Connections tab, enable Username-Password-Authentication (or the connection the admin uses) so members can sign in.

Update values.yaml with the admin’s email. The platform chart’s post-install hook looks up this user and makes it the organization’s owner.

values.yaml -> auth0:

yaml
platformAdminEmail: "<admin-email>"

9. Deploy the Auth0 Actions

Two Actions in the deployment repository’s scripts/auth0/actions/ directory add the platform’s organization, role, and service-account claims to tokens. The auth0-bootstrap chart creates, deploys, and binds both. To add them by hand, create each in the Auth0 Dashboard under Actions > Library, deploy it, and add it to its trigger:

Action sourceTriggerSecrets and dependencies
post-login-users-and-service-accounts.jsLogin (post-login)AUTH_SERVICE_URL: https://<domain>/authService; AUTH_SERVICE_API_SECRET: the global.secrets.authService.values.apiSecret value
service-account-credentials.jsMachine to Machine (credentials-exchange)domain: <auth0-domain>; clientId and clientSecret: an M2M application’s credentials; dependency auth0 version 3.x

For service-account-credentials.js, create a separate M2M application named Guardian Platform Actions, authorized for the Auth0 Management API with only the read:users and read:users_app_metadata scopes, as the bootstrap does.

Alternative: Web UI Setup

Click to expand Auth0 Dashboard instructions

Using the Auth0 Dashboard

1. Create Platform API Application

  • Navigate to Applications > Applications in Auth0 Dashboard
  • Click “Create Application”
  • Name: “EQTY Platform API”
  • Type: Machine to Machine Applications
  • Authorize it to call the Auth0 Management API
  • Grant scope: read:organizations
  • Save the Client ID and Client Secret

Update secrets.yaml with the Platform API credentials.

secrets.yaml -> global.secrets.auth.auth0.values:

yaml
clientId: "<platform-api-client-id>"
clientSecret: "<platform-api-client-secret>"

2. Create Auth Service Backend Application

  • Create another M2M application: “EQTY Auth Service Backend”
  • Authorize it to call the Auth0 Management API
  • Grant the scopes listed in step 3 of the CLI method
  • Save the Client ID and Client Secret

Update secrets.yaml with the Auth Service Backend credentials.

secrets.yaml -> global.secrets.auth.auth0.values:

yaml
mgmtClientId: "<auth-service-client-id>"
mgmtClientSecret: "<auth-service-client-secret>"

3. Create Guardian SPA

  • Create a Single Page Application: “Guardian Platform Frontend”
  • Allowed Callback URLs, Allowed Logout URLs, and Allowed Web Origins: https://<domain>, http://localhost:5173
  • Change the application’s login experience to “Business Users”
  • Save the Client ID

Update values.yaml with the domain and SPA client ID.

values.yaml -> auth-service.config.idp:

yaml
issuer: "https://<auth0-domain>/"
auth0:
  domain: "<auth0-domain>"
  managementAudience: "https://<auth0-domain>/api/v2/"

values.yaml -> governance-service.config:

yaml
auth0Domain: "<auth0-domain>"

values.yaml -> guardian.config:

yaml
auth0Domain: "<auth0-domain>"
auth0ClientId: "<spa-client-id>"

4. Create API Definition

  • Navigate to Applications > APIs
  • Create API with identifier: https://<domain>
  • Add permissions: governance:declarations:create, integrity:statements:create

Update values.yaml with the API identifier.

values.yaml -> auth-service.config.idp.auth0:

yaml
apiIdentifier: "https://<domain>"

values.yaml -> guardian.config:

yaml
auth0Audience: "https://<domain>"

5. Create Governance Worker Application

  • Create another M2M application: “EQTY Governance Worker”
  • Authorize it to call the API from step 4, with both of its permissions
  • Save the Client ID and Client Secret
  • Generate an encryption key: openssl rand -base64 32

Update secrets.yaml with the worker credentials.

secrets.yaml -> global.secrets.governanceWorker.values:

yaml
encryptionKey: "<generated-key>"
clientId: "<worker-client-id>"
clientSecret: "<worker-client-secret>"

6. Create the Organization and Actions

  • Navigate to Organizations, create an organization named guardian, add the platform admin as a member, and enable Username-Password-Authentication on its Connections tab
  • Set auth0.platformAdminEmail in values.yaml to the admin’s email
  • Deploy the two Actions as described in step 9 of the CLI method

Verification

Verify the OIDC discovery endpoint. The response’s issuer field must equal https://<auth0-domain>/:

bash
curl "https://<auth0-domain>/.well-known/openid-configuration"

Test token issuance with the Governance Worker credentials:

bash
curl -X POST "https://<auth0-domain>/oauth/token" \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": "<worker-client-id>",
    "client_secret": "<worker-client-secret>",
    "audience": "https://<domain>",
    "grant_type": "client_credentials"
  }'

If the first command returns the discovery document and the second returns an access_token, the Auth0 configuration is correct.

Security Best Practices

  • Rotate Secrets: Regularly rotate client secrets, update the Kubernetes secrets, and restart Auth Service so it picks up the new Governance Worker secret
  • Least Privilege: Grant only the Management API scopes this guide lists. Once a bootstrap run has completed with the Guardian Platform Actions application, no Action uses the bootstrap M2M application, so you can remove its scopes, and add them back before re-running the bootstrap. Until then, the credentials-exchange Action still uses it, and removing its scopes drops the service-account claims from Governance Worker tokens
  • Organizations: Use Auth0 Organizations for tenant isolation in multi-tenant deployments
  • Monitor Activity: Enable Auth0 log streaming to monitor authentication events and detect anomalies

Troubleshooting

  • Failed to get Management API token — Solution: Check the auth0-management credentials, that the bootstrap M2M is authorized for the Auth0 Management API, and that auth0.domain has no https:// or trailing slash
  • Action fails with HTTP 403 insufficient_scope — Solution: Add the four actions scopes from quick-start step 1 to the bootstrap M2M and re-run
  • Failed to prepare Actions application (HTTP 403) — Solution: Add update:client_keys to the bootstrap M2M and re-run; the bootstrap rotates the Guardian Platform Actions secret when the saved one is missing or a token request with it fails
  • actions.postLogin.authService.url (or urlDev) is required — Solution: Set actions.postLogin.authService.url in bootstrap-values.yaml, as in step 3
  • The auth0-management Secret has no auth-service-api-secret key — Solution: Add the key with the Auth Service API secret, as in step 2, and re-run
  • No client secret in the credentials secret — Solution: The application already existed; copy or rotate its secret in the Auth0 Dashboard
  • Failed to create user (HTTP 400) — Solution: The platform-admin password fails the tenant’s password policy (Authentication > Database > Username-Password-Authentication > Password Policy); recreate the secret with a stronger password and re-run. If the tenant uses another database connection, set users.admin.connection

Official Documentation