Service Account Setup
This document describes how to set up service accounts for the governance worker to create declarations without user context.
Overview
Section titled “Overview”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.
Architecture
Section titled “Architecture”- Auth Service - Manages service accounts and their tokens
- Governance Service - Uses service account tokens when running in worker context
- Integrity Service - Validates tokens and retrieves DID keys from the configured key-management provider
- Auth0 - Issues the M2M token directly; claim enrichment is handled by the Auth0 Client Credentials Exchange Action
- Keycloak / Entra - Issue the raw M2M token; auth-service then enriches it with platform-access claims via token exchange
Setup Steps
Section titled “Setup Steps”1. Create Auth0 M2M Application
Section titled “1. Create Auth0 M2M Application”- Go to Auth0 Dashboard > Applications
- Click “Create Application”
- Name it “Governance Worker Service Account”
- Select “Machine to Machine Applications”
- Select your API (the one your governance service uses)
- Grant necessary scopes (at minimum:
read:users,create:declarations) - Save the Client ID and Client Secret
2. Environment Configuration
Section titled “2. Environment Configuration”Add these environment variables to both services:
Auth Service (.env) — pick the block for your active IDP:
# Auth0AUTH0_GOVERNANCE_WORKER_CLIENT_ID=<your-m2m-client-id>AUTH0_GOVERNANCE_WORKER_CLIENT_SECRET=<your-m2m-client-secret>
# KeycloakKEYCLOAK_GOVERNANCE_WORKER_CLIENT_ID=governance-workerKEYCLOAK_GOVERNANCE_WORKER_CLIENT_SECRET=<your-client-secret>
# EntraENTRA_GOVERNANCE_WORKER_CLIENT_ID=<your-app-client-id>ENTRA_GOVERNANCE_WORKER_CLIENT_SECRET=<your-client-secret>Governance Service (.env):
# Auth service integration for service accountsAUTH_SERVICE_URL=http://localhost:8080AUTH_SERVICE_API_KEY=<auth-service-api-secret> # Matches the auth service's API_SECRET value3. Database Migration
Section titled “3. Database Migration”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.
4. Service Account Creation
Section titled “4. Service Account Creation”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_IDand*_GOVERNANCE_WORKER_CLIENT_SECRETmust 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. - Keycloak —
KEYCLOAK_GOVERNANCE_WORKER_CLIENT_IDdefaults togovernance-workerwhen unset. IfKEYCLOAK_GOVERNANCE_WORKER_CLIENT_SECRETis 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 setKEYCLOAK_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:
# Retrieve a token for the bootstrapped governance-worker accountcurl http://localhost:8080/api/v1/service-accounts/governance-worker/token \ -H "Authorization: Bearer <your-api-secret>"5. Auth0 Actions Setup
Section titled “5. Auth0 Actions Setup”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:
- Go to Auth0 Dashboard > Actions > Flows > Login
- Edit your post-login action
- Install the post-login action supplied with the Governance Platform deployment package or bootstrap chart for your release
- 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:
- Create a new action for the M2M / Client Credentials Exchange flow
- Follow the public Auth0 deployment documentation for your release to configure the Client Credentials Exchange flow
- 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
6. Testing
Section titled “6. Testing”- Verify Service Account Creation:
# Check if governance-worker account was createdcurl 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:
API_SECRET=<your-api-secret> ./auth-service/scripts/keycloak/test-service-account-enrichment.sh- 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
- 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”
How It Works
Section titled “How It Works”Service Account Creation (on auth service startup)
Section titled “Service Account Creation (on auth service startup)”- Auth service starts and checks for
*_GOVERNANCE_WORKER_CLIENT_ID/SECRETfor the active IDP - Creates / syncs the IDP user or client with service-account metadata
- Creates local user record in the database
- Reconciles DID key in the configured key-management provider for the service account
Runtime Operation
Section titled “Runtime Operation”- Worker starts evaluation without user context
- Evaluator detects missing token and requests service account token
- Auth service returns JWT with platform-wide access claims:
https://governance.eqtylab.io/platform_access: truehttps://governance.eqtylab.io/is_service_account: truehttps://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
- Evaluator adds token to context and resolves identity
- Governance service creates declaration using service account identity
- Integrity service validates token and retrieves the DID key from the configured key-management provider
Platform-Wide Access
Section titled “Platform-Wide Access”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.
Troubleshooting
Section titled “Troubleshooting”Service account not created
Section titled “Service account not created”- For Auth0 and Entra, verify both
*_GOVERNANCE_WORKER_CLIENT_IDand*_GOVERNANCE_WORKER_CLIENT_SECRETare set - For Keycloak, verify
KEYCLOAK_GOVERNANCE_WORKER_CLIENT_SECRETis 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
Token retrieval fails
Section titled “Token retrieval fails”- Verify
AUTH_SERVICE_API_KEY(governance-service) matches the auth service’sAPI_SECRETvalue - Check network connectivity between services
- Ensure auth service is running
- The endpoint
/api/v1/service-accounts/{serviceName}/tokenrequiresAuthorization: Bearer <API_SECRET>header
Declaration creation fails
Section titled “Declaration creation fails”- 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
DID key not found
Section titled “DID key not found”- 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)
Security Considerations
Section titled “Security Considerations”- API Key Protection: Store API keys securely, rotate regularly
- Network Security: Use TLS for service-to-service communication in production
- Token Expiry: Service account token lifetime follows the upstream IDP machine-to-machine token
expires_invalue used during enrichment - Audit Trail: All service account actions are logged
Implementation Details
Section titled “Implementation Details”Service Account Token Structure
Section titled “Service Account Token Structure”Service account tokens are JWT tokens with the following claims:
sub: Platform user UUID for the governance-worker service accounthttps://governance.eqtylab.io/name: Service account namehttps://governance.eqtylab.io/user_id: Platform user UUID associated with the service accounthttps://governance.eqtylab.io/platform_access:truefor the governance-worker service accounthttps://governance.eqtylab.io/is_service_account:truehttps://governance.eqtylab.io/service_name: Service name, such asgovernance-workerhttps://governance.eqtylab.io/did_key_name: Configured DID key name, when availableexp: Expiration time inherited from the upstream IDP token responseiat: Issued at timeiss: 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 Configuration
Section titled “Helm Chart Configuration”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.
Future Enhancements
Section titled “Future Enhancements”- Token Rotation: Implement automatic token rotation before expiry
- Scoped Permissions: Add role-based access control for service accounts
- Multiple Organizations: Support service accounts across multiple orgs
- Monitoring: Add metrics for service account usage