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 Package CR 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 Package CR
Configure 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 Package CR
Terminal 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
- Package CR reference — full SSO client and
protocolMappersfield specification
Next steps
Section titled “Next steps”These guides and concepts may be useful to explore next: