Skip to content

Service Account Setup

This document describes how to set up service accounts for the governance worker to create declarations without user context.

The governance worker runs scheduled indicator evaluations that may need to create declarations when compliance failures are detected. Since the worker runs without user context, it needs a service account with its own DID key to authenticate with the integrity service.

The governance-worker service account is created with platform-wide access, meaning it can create declarations for any organization in the system. This is necessary because the worker processes evaluations across all organizations.

  1. Auth Service - Manages service accounts and their tokens
  2. Governance Service - Uses service account tokens when running in worker context
  3. Integrity Service - Validates tokens and retrieves DID keys from the configured key-management provider
  4. Auth0 - Issues the M2M token directly; claim enrichment is handled by the Auth0 Client Credentials Exchange Action
  5. Keycloak / Entra - Issue the raw M2M token; auth-service then enriches it with platform-access claims via token exchange
  1. Go to Auth0 Dashboard > Applications
  2. Click “Create Application”
  3. Name it “Governance Worker Service Account”
  4. Select “Machine to Machine Applications”
  5. Select your API (the one your governance service uses)
  6. Grant necessary scopes (at minimum: read:users, create:declarations)
  7. Save the Client ID and Client Secret

Add these environment variables to both services:

Auth Service (.env) — pick the block for your active IDP:

Terminal window
# Auth0
AUTH0_GOVERNANCE_WORKER_CLIENT_ID=<your-m2m-client-id>
AUTH0_GOVERNANCE_WORKER_CLIENT_SECRET=<your-m2m-client-secret>
# Keycloak
KEYCLOAK_GOVERNANCE_WORKER_CLIENT_ID=governance-worker
KEYCLOAK_GOVERNANCE_WORKER_CLIENT_SECRET=<your-client-secret>
# Entra
ENTRA_GOVERNANCE_WORKER_CLIENT_ID=<your-app-client-id>
ENTRA_GOVERNANCE_WORKER_CLIENT_SECRET=<your-client-secret>

Governance Service (.env):

Terminal window
# Auth service integration for service accounts
AUTH_SERVICE_URL=http://localhost:8080
AUTH_SERVICE_API_KEY=<auth-service-api-secret> # Matches the auth service's API_SECRET value

The service_accounts table is provisioned by the initial auth-service schema migration (000001_complete_auth_schema.up.sql). Service accounts have organization_id = NULL for platform-wide access. Tokens are minted on demand and not stored in the database.

The auth service bootstraps the governance-worker service account on startup. Bootstrapping includes:

  • Creating (or syncing) the IDP-side user/client for the worker
  • Creating the user record in the auth-service database
  • Reconciling the service account’s DID key in the configured key-management provider

Credential requirements depend on the active IDP:

  • Auth0 / Entra — both *_GOVERNANCE_WORKER_CLIENT_ID and *_GOVERNANCE_WORKER_CLIENT_SECRET must be set; the M2M client/app registration must already exist in the IDP. If either value is missing, startup skips creation and logs a warning.
  • KeycloakKEYCLOAK_GOVERNANCE_WORKER_CLIENT_ID defaults to governance-worker when unset. If KEYCLOAK_GOVERNANCE_WORKER_CLIENT_SECRET is also unset, auth-service provisions the client via the Keycloak admin API and captures the generated secret. If the client already exists in Keycloak, startup fails with a message instructing you to set KEYCLOAK_GOVERNANCE_WORKER_CLIENT_SECRET.

Existing service accounts are still resynced when bootstrap is skipped.

Custom service-account creation is currently handled by auth-service bootstrap and internal service logic. The only public service-account route exposed by auth-service is token retrieval for an existing service account:

Terminal window
# Retrieve a token for the bootstrapped governance-worker account
curl http://localhost:8080/api/v1/service-accounts/governance-worker/token \
-H "Authorization: Bearer <your-api-secret>"

You need to set up two Auth0 actions for complete service account support:

A. Post-Login Action (for regular user logins)

Section titled “A. Post-Login Action (for regular user logins)”

Replace your existing Auth0 post-login action with the version that supports service accounts:

  1. Go to Auth0 Dashboard > Actions > Flows > Login
  2. Edit your post-login action
  3. Install the post-login action supplied with the Governance Platform deployment package or bootstrap chart for your release
  4. Deploy the changes

The updated action:

  • Detects service accounts by checking app_metadata.is_service_account
  • Adds required claims for service accounts (auth0_user_id, sub, organization_id)
  • Skips email verification for service accounts

B. Client Credentials Exchange Action (for M2M tokens)

Section titled “B. Client Credentials Exchange Action (for M2M tokens)”

This is required for the governance-worker to get properly enriched tokens:

  1. Create a new action for the M2M / Client Credentials Exchange flow
  2. Follow the public Auth0 deployment documentation for your release to configure the Client Credentials Exchange flow
  3. Install the service-account credentials action supplied with the Governance Platform deployment package or bootstrap chart for your release

The M2M action:

  • Enriches M2M tokens with service account user information
  • Adds platform access claims for governance-worker
  • Includes DID key information from user metadata
  1. Verify Service Account Creation:
Terminal window
# Check if governance-worker account was created
curl http://localhost:8080/api/v1/service-accounts/governance-worker/token \
-H "Authorization: Bearer <your-api-secret>"

For Keycloak deployments, the helper script verifies token enrichment end-to-end:

Terminal window
API_SECRET=<your-api-secret> ./auth-service/scripts/keycloak/test-service-account-enrichment.sh
  1. Test Declaration Creation:
  • Create a scheduled indicator evaluation that will fail
  • Wait for the worker to process it
  • Check if a declaration was created with “System” as the submitter
  1. Monitor Logs: Look for these log messages:
  • “Service account client initialized for worker authentication”
  • “No user token in context, using service account authentication”
  • “Successfully obtained service account token”

Service Account Creation (on auth service startup)

Section titled “Service Account Creation (on auth service startup)”
  1. Auth service starts and checks for *_GOVERNANCE_WORKER_CLIENT_ID/SECRET for the active IDP
  2. Creates / syncs the IDP user or client with service-account metadata
  3. Creates local user record in the database
  4. Reconciles DID key in the configured key-management provider for the service account
  1. Worker starts evaluation without user context
  2. Evaluator detects missing token and requests service account token
  3. Auth service returns JWT with platform-wide access claims:
    • https://governance.eqtylab.io/platform_access: true
    • https://governance.eqtylab.io/is_service_account: true
    • https://governance.eqtylab.io/service_name: "governance-worker"
    • https://governance.eqtylab.io/name: "governance-worker"
    • https://governance.eqtylab.io/user_id: <platform-user-uuid>
    • https://governance.eqtylab.io/did_key_name: <configured-key-name> when a DID key is reconciled for the service account
  4. Evaluator adds token to context and resolves identity
  5. Governance service creates declaration using service account identity
  6. Integrity service validates token and retrieves the DID key from the configured key-management provider

The governance-worker service account is created without an organization binding. Auth service represents that platform-wide capability through namespaced service-account claims rather than wildcard organization membership:

  • No organization_id: Service account is not tied to any specific organization
  • Special claims: Token includes https://governance.eqtylab.io/platform_access: true
  • Identity context: The service-account token is not enriched with wildcard organization or role arrays
  • Declaration creation: Can create declarations for any project policy control across all organizations

This design ensures the worker can process evaluations and create declarations for all organizations without needing separate credentials for each.

  • For Auth0 and Entra, verify both *_GOVERNANCE_WORKER_CLIENT_ID and *_GOVERNANCE_WORKER_CLIENT_SECRET are set
  • For Keycloak, verify KEYCLOAK_GOVERNANCE_WORKER_CLIENT_SECRET is set, or that auth-service has admin permissions to provision the client
  • Verify database connection and migrations
  • Check auth service logs for skip warnings or errors
  • Verify AUTH_SERVICE_API_KEY (governance-service) matches the auth service’s API_SECRET value
  • Check network connectivity between services
  • Ensure auth service is running
  • The endpoint /api/v1/service-accounts/{serviceName}/token requires Authorization: Bearer <API_SECRET> header
  • Verify Auth0 action is updated and deployed
  • Check the service account has a DID key in the configured key-management provider
  • Review integrity service logs for authentication errors
  • Check the configured key-management provider (Azure Key Vault, AWS KMS, GCP KMS, or mock) for the service account’s key
  • Verify credentials for the key-management provider in the integrity service
  • Confirm the service account’s DID key was reconciled on startup (check auth-service logs)
  1. API Key Protection: Store API keys securely, rotate regularly
  2. Network Security: Use TLS for service-to-service communication in production
  3. Token Expiry: Service account token lifetime follows the upstream IDP machine-to-machine token expires_in value used during enrichment
  4. Audit Trail: All service account actions are logged

Service account tokens are JWT tokens with the following claims:

  • sub: Platform user UUID for the governance-worker service account
  • https://governance.eqtylab.io/name: Service account name
  • https://governance.eqtylab.io/user_id: Platform user UUID associated with the service account
  • https://governance.eqtylab.io/platform_access: true for the governance-worker service account
  • https://governance.eqtylab.io/is_service_account: true
  • https://governance.eqtylab.io/service_name: Service name, such as governance-worker
  • https://governance.eqtylab.io/did_key_name: Configured DID key name, when available
  • exp: Expiration time inherited from the upstream IDP token response
  • iat: Issued at time
  • iss: Issuer (auth service URL)

The enriched service-account token may also retain non-conflicting claims from the upstream IDP token. Auth service does not add wildcard organizations or platform role arrays to the service-account JWT.

Helm chart values for Auth Service and the full Governance Platform are documented in the public deployment documentation for each release. Use those release-specific values when configuring service-account support.

  1. Token Rotation: Implement automatic token rotation before expiry
  2. Scoped Permissions: Add role-based access control for service accounts
  3. Multiple Organizations: Support service accounts across multiple orgs
  4. Monitoring: Add metrics for service account usage