Auth0
Configure Auth0 for deployment
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) kubectlandhelmaccess to the deployment namespacejq1.6 or later, to read the bootstrap’s generated credentials- A clone of the deployment repository for the
auth0-bootstrapchart 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:
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:
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>"Quick Start (Bootstrap Chart - Recommended)
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:
- Go to Applications > Applications > Create Application, choose Machine to Machine Applications, and name it
Governance Bootstrap M2M - Authorize it for the Auth0 Management API with these scopes:
read:clients,create:clients,update:clientsread:resource_servers,create:resource_servers,update:resource_serversread:client_grants,create:client_grants,update:client_grantsread:users,create:users,update:usersread:actions,create:actions,update:actions,delete:actionsupdate:client_keys
- 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
# 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:
cp charts/auth0-bootstrap/examples/values.yaml bootstrap-values.yamlEdit bootstrap-values.yaml, replacing the CHANGE_ME placeholders. <domain> is the Guardian Platform domain:
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:
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 10mInstead 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:
kubectl logs job/auth0-bootstrap-auth0-bootstrap --namespace guardian -fThe 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:
kubectl get secret auth0-bootstrap-credentials --namespace guardian -o json \
| jq '.data | map_values(@base64d)'| Key | Value |
|---|---|
frontend-client-id | Guardian Platform Frontend client ID |
backend-client-id, backend-client-secret | Guardian Platform Backend credentials |
worker-client-id, worker-client-secret | Governance Worker credentials |
actions-client-id, actions-client-secret | Guardian Platform Actions credentials, kept so later runs reuse them |
auth0-domain | The 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:
- Under Organizations, create an organization named
guardian(theauth0.organizationNamevalue) - On its Members tab, add the admin user the bootstrap created
- On its Connections tab, enable
Username-Password-Authentication - 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.domainfrombootstrap-values.yamlhttps://<domain>:auth0.api.identifier<spa-client-id>:frontend-client-idplatform-auth0values:backend-client-idandbackend-client-secret, for both theclientId/clientSecretandmgmtClientId/mgmtClientSecretpairsgovernanceWorkervalues:worker-client-idandworker-client-secret<worker-encryption-key>and<jwt-secret>: generate each withopenssl rand -base64 32<auth-service-api-secret>: theauth-service-api-secretfrom step 2<admin-email>:users.admin.email
Read the auth-service-api-secret back with:
kubectl get secret auth0-management --namespace guardian \
-o jsonpath='{.data.auth-service-api-secret}' | base64 -dThen 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:
# macOS
brew install auth0/auth0-cli/auth0
# Linux
curl -sSfL https://raw.githubusercontent.com/auth0/auth0-cli/main/install.sh | shAuthenticate with the Auth0 tenant:
auth0 loginNote 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:
issuer: "https://<auth0-domain>/"
auth0:
domain: "<auth0-domain>"
managementAudience: "https://<auth0-domain>/api/v2/"values.yaml -> governance-service.config:
auth0Domain: "<auth0-domain>"values.yaml -> guardian.config:
auth0Domain: "<auth0-domain>"values.yaml -> auth0:
domain: "<auth0-domain>"2. Create Platform API (M2M Application)
auth0 apps create \
--name "EQTY Platform API" \
--type m2m \
--description "Backend API for Guardian Platform token validation" \
--reveal-secretsThe output will look like:
=== your-tenant.us.auth0.com application created
CLIENT ID abc123def456
NAME EQTY Platform API
TYPE Machine to Machine
CLIENT SECRET your-client-secret-hereSave 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:
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:
clientId: "<platform-api-client-id>"
clientSecret: "<platform-api-client-secret>"3. Create Auth Service Backend (M2M Application)
auth0 apps create \
--name "EQTY Auth Service Backend" \
--type m2m \
--description "User management for Guardian Platform" \
--reveal-secretsSave both the Client ID and Client Secret.
Grant the required Management API permissions. Replace <auth-service-client-id> with the Client ID from above:
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:
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:
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-secretsSave the Client ID from the output.
Update values.yaml with the SPA client ID.
values.yaml -> guardian.config:
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:
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:
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:
apiIdentifier: "https://<domain>"values.yaml -> guardian.config:
auth0Audience: "https://<domain>"7. Create Governance Worker (M2M Application)
The Governance Worker is the platform’s service account for automated governance workflows.
auth0 apps create \
--name "EQTY Governance Worker" \
--type m2m \
--description "Service account for Guardian Platform workflows" \
--reveal-secretsSave 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:
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:
openssl rand -base64 32Update secrets.yaml with the worker credentials.
secrets.yaml -> global.secrets.governanceWorker.values:
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:
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:
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:
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 source | Trigger | Secrets and dependencies |
|---|---|---|
post-login-users-and-service-accounts.js | Login (post-login) | AUTH_SERVICE_URL: https://<domain>/authService; AUTH_SERVICE_API_SECRET: the global.secrets.authService.values.apiSecret value |
service-account-credentials.js | Machine 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:
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:
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:
issuer: "https://<auth0-domain>/"
auth0:
domain: "<auth0-domain>"
managementAudience: "https://<auth0-domain>/api/v2/"values.yaml -> governance-service.config:
auth0Domain: "<auth0-domain>"values.yaml -> guardian.config:
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:
apiIdentifier: "https://<domain>"values.yaml -> guardian.config:
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:
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 enableUsername-Password-Authenticationon its Connections tab - Set
auth0.platformAdminEmailinvalues.yamlto 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>/:
curl "https://<auth0-domain>/.well-known/openid-configuration"Test token issuance with the Governance Worker credentials:
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 theauth0-managementcredentials, that the bootstrap M2M is authorized for the Auth0 Management API, and thatauth0.domainhas nohttps://or trailing slash- Action fails with
HTTP 403insufficient_scope— Solution: Add the fouractionsscopes from quick-start step 1 to the bootstrap M2M and re-run Failed to prepare Actions application (HTTP 403)— Solution: Addupdate:client_keysto 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 failsactions.postLogin.authService.url (or urlDev) is required— Solution: Setactions.postLogin.authService.urlinbootstrap-values.yaml, as in step 3The 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: Theplatform-adminpassword 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, setusers.admin.connection
Official Documentation
- Auth0 CLI Documentation
- Auth0 Management API Reference
- Auth0 Applications Overview
- Auth0 Organizations (Multi-tenancy)
- Auth0 Actions
- Helm Chart Examples
Related
- Authentication — how Auth0 compares with the other supported providers
- Identity Providers — what Auth Service supports in Auth0 mode
- Chart Values — the full values and secrets files these settings go into
- Helm Deployment — deploying the platform once the provider is configured