Configure service account clients
What you’ll accomplish
Section titled “What you’ll accomplish”You’ll configure a Keycloak client using the OAuth 2.0 Client Credentials Grant so that automated processes (CI/CD pipelines, backend services, and scripts) can obtain tokens and access SSO-protected applications without a user session.
Prerequisites
Section titled “Prerequisites”- UDS Core deployed
- UDS CLI installed
- A UDS
PackageCR for the workload that needs machine-to-machine access - The
clientIdof the target SSO-protected application (used as the token audience)
Before you begin
Section titled “Before you begin”Service account tokens (Client Credentials Grant) are designed for machine-to-machine authentication where there is no interactive user. Key characteristics:
- Tokens have a
service-account-username prefix and include aclient_idclaim - The
aud(audience) claim is not set by default. You must add an audience mapper to allow the token to access a specific SSO-protected application. serviceAccountsEnabled: truerequiresstandardFlowEnabled: falseand is incompatible withpublicClient: true
-
Add a service account client to the
PackageCRConfigure an SSO client with
serviceAccountsEnabled: trueand an audience mapper pointing to the target Authservice client:package.yaml apiVersion: uds.dev/v1alpha1kind: Packagemetadata:name: my-automationnamespace: argospec:sso:- name: httpbin-api-clientclientId: httpbin-api-clientstandardFlowEnabled: falseserviceAccountsEnabled: trueprotocolMappers:- name: audienceprotocol: "openid-connect"protocolMapper: "oidc-audience-mapper"config:# Set to the clientId of the Authservice-protected applicationincluded.client.audience: "uds-core-httpbin"access.token.claim: "true"introspection.token.claim: "true"id.token.claim: "false"lightweight.claim: "false"userinfo.token.claim: "false" -
Apply the
PackageCRTerminal window uds zarf tools kubectl apply -f package.yamlThe UDS Operator creates the Keycloak client and stores the client secret in a Kubernetes secret in the application namespace.
-
Retrieve the client secret
The client secret is stored in a Kubernetes secret named
sso-client-<client-id>:Terminal window # Linuxuds zarf tools kubectl get secret -n <namespace> sso-client-<client-id> -o jsonpath='{.data.secret}' | base64 -d# macOSuds zarf tools kubectl get secret -n <namespace> sso-client-<client-id> -o jsonpath='{.data.secret}' | base64 -D -
(Optional) Configure multiple audiences
If a service account token needs access to multiple Authservice-protected applications, add separate audience mappers for each target.
package.yaml spec:sso:- name: multi-target-clientclientId: multi-target-clientstandardFlowEnabled: falseserviceAccountsEnabled: truedefaultClientScopes:- openidprotocolMappers:- name: audience-app-1protocol: "openid-connect"protocolMapper: "oidc-audience-mapper"config:included.custom.audience: "uds-core-app-1"access.token.claim: "true"introspection.token.claim: "true"id.token.claim: "true"lightweight.claim: "true"userinfo.token.claim: "true"- name: audience-app-2protocol: "openid-connect"protocolMapper: "oidc-audience-mapper"config:included.custom.audience: "uds-core-app-2"access.token.claim: "true"introspection.token.claim: "true"id.token.claim: "true"lightweight.claim: "true"userinfo.token.claim: "true"
Verification
Section titled “Verification”Confirm the service account client is configured correctly:
- Log in to the Keycloak admin UI (uds realm)
- Go to Clients and find your client ID
- Verify Service accounts roles is On and Standard flow is Off
Test token retrieval:
# Replace <domain>, <client-id>, and <client-secret> with your valuescurl -s -X POST \ "https://sso.<domain>/realms/uds/protocol/openid-connect/token" \ -d "grant_type=client_credentials" \ -d "client_id=<client-id>" \ -d "client_secret=<client-secret>" \ | jq .A successful response includes an access_token. Verify the aud claim includes the expected audience:
# Extract and decode the access token payload# Linuxecho "<access_token>" | cut -d. -f2 | base64 -d 2>/dev/null | jq .aud# macOSecho "<access_token>" | cut -d. -f2 | base64 -D 2>/dev/null | jq .audAlternatively, paste the token into jwt.io for a visual breakdown.
Troubleshooting
Section titled “Troubleshooting”Problem: 401 when accessing an Authservice-protected application
Section titled “Problem: 401 when accessing an Authservice-protected application”Symptoms: Token is obtained successfully but the application returns 401.
Solution: Verify the audience mapper is pointing to the correct target. The included.client.audience value must match the clientId of the target application’s Authservice SSO client, not this service account client’s own clientId.
Check the decoded token’s aud claim, or paste it into jwt.io to inspect it visually:
# Decode the access token payload (replace TOKEN with the actual token value)# Linuxecho "TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jq .aud# macOSecho "TOKEN" | cut -d. -f2 | base64 -D 2>/dev/null | jq .audProblem: serviceAccountsEnabled: true rejected by the operator
Section titled “Problem: serviceAccountsEnabled: true rejected by the operator”Symptoms: Package CR fails to apply with a validation error.
Solution: Ensure standardFlowEnabled is set to false and publicClient is not set to true. Both are incompatible with service accounts:
sso: - name: my-service-client clientId: my-service-client standardFlowEnabled: false # Required serviceAccountsEnabled: true # publicClient: true # Do not set; incompatible with service accountsProblem: Client secret is not found in the namespace
Section titled “Problem: Client secret is not found in the namespace”Symptoms: The expected Kubernetes secret does not exist after applying the Package CR.
Solution: Check the UDS Operator logs for errors during client creation:
uds zarf tools kubectl logs -n pepr-system -l app=pepr-uds-core-watcher --tail=50 | grep <client-id>Related documentation
Section titled “Related documentation”- OAuth 2.0 Client Credentials Grant - specification for the service account flow
PackageCR reference - full SSO client andprotocolMappersfield specification- Configure OAuth 2.0 device flow - Enable device authorization for CLI tools and headless apps.
- Protect non-OIDC apps with SSO - Add SSO protection to applications that have no native OIDC support.