This guide walks you through integrating ArgoCD with Keycloak as a Single Sign-On (SSO) Identity Provider using ArgoCD’s native OpenID Connect (OIDC) support — no Dex required. Along the way, we’ll map Keycloak groups to ArgoCD RBAC roles for fine-grained access control.
This document focuses purely on the integration itself. For the Keycloak installation, refer to the separate guide: Deploying Keycloak 26 in Production on AlmaLinux 9.
Placeholders used throughout this guide: Wherever you see values like
sso.example.com,argocd.example.com, orYOUR_CLIENT_SECRET, replace them with your own actual values.
| Placeholder | Description | Example |
|---|---|---|
sso.example.com | Your Keycloak FQDN | sso.company.net |
argocd.example.com | Your ArgoCD FQDN | argocd.company.net |
YOUR_CLIENT_SECRET | The OIDC client secret from Keycloak | Copy from the Credentials tab |
YOUR_REALM | Your Keycloak realm name | internal |
Table of Contents
- Prerequisites
- Why Native OIDC Instead of Dex
- Create a Client for ArgoCD in Keycloak
- Add a Groups Mapper
- Create Groups in Keycloak
- Store the Client Secret in a Kubernetes Secret
- Configure argocd-cm
- Configure argocd-rbac-cm
- Restart & Test Login
- Break-Glass Account (Local Admin)
- Offboarding
- Troubleshooting
1. Prerequisites
- Keycloak is up and running, accessible over HTTPS (e.g.,
https://sso.example.com), with a realm already created (see the GitLab integration guide for realm creation steps). - ArgoCD is installed on your Kubernetes cluster and accessible over HTTPS (e.g.,
https://argocd.example.com). - Admin access to the Keycloak Admin Console.
kubectlaccess with edit permissions on the namespace where ArgoCD is running (e.g.,argocd).
2. Why Native OIDC Instead of Dex
ArgoCD supports two approaches for enabling SSO:
- Dex — a built-in authentication proxy that can aggregate multiple identity providers (LDAP, GitHub, SAML, OIDC, etc.) into a single configuration.
- Native OIDC (
oidc.configdirectly inargocd-cm) — ArgoCD communicates directly with the Identity Provider without an intermediary.
Since Keycloak already supports OIDC natively and is the only Identity Provider in use, native OIDC is the simpler choice — there’s no need to run an additional Dex component in the cluster, which means fewer moving parts and fewer points of failure. Dex only becomes relevant if you need to aggregate multiple Identity Providers simultaneously.
3. Create a Client for ArgoCD in Keycloak
- Log in to
https://sso.example.com/admin/and switch to your realm (e.g.,YOUR_REALM). - Navigate to Clients → Create client.
- General Settings
- Client type:
OpenID Connect - Client ID:
argocd - Click Next.
- Client type:
- Capability config
- Client authentication:
On - Authentication flow: check Standard flow only.
- Click Next, then Save.
- Client authentication:
- On the Settings page:
- Valid redirect URIs:
https://argocd.example.com/auth/callback - Valid post logout redirect URIs:
https://argocd.example.com/* - Web origins:
https://argocd.example.com
- Valid redirect URIs:
- Click Save.
- Open the Credentials tab and copy the Client secret — you’ll need it in step 6.
4. Add a Groups Mapper
By default, the groups claim is not automatically included in Keycloak’s OIDC tokens. ArgoCD needs this claim to match users to RBAC roles, so it must be added manually.
- In your realm, navigate to Client scopes → Create client scope.
- Name:
groups - Type:
Default(not Optional — this ensures thegroupsclaim is always included in tokens without the client needing to explicitly request it) - Click Save.
- Name:

- Enter the newly created
groupsclient scope → Mappers tab → Add mapper → By configuration → Group Membership.- Name:
groups - Token Claim Name:
groups - Full group path:
Off(so the output is a plain group name likeargocd-adminsrather than/argocd-adminswith the full path) - Add to ID token:
On - Add to access token:
On - Click Save.
- Name:
- Go back to the
argocdclient → Client scopes tab → verify that thegroupsscope is already assigned (since it was created as a Default scope, it should automatically appear for all clients).
5. Create Groups in Keycloak
Create the groups that will be mapped to ArgoCD roles. Here’s an example with three access tiers:
- Navigate to Groups → Create group, and create three groups:
argocd-adminsargocd-developersargocd-viewers

- Assign users to the appropriate groups via Users → select a user → Groups tab → Join group.
6. Store the Client Secret in a Kubernetes Secret
The client secret should never be written as plain text in a ConfigMap. Instead, store it in the argocd-secret Secret that ArgoCD already uses:
kubectl -n argocd patch secret argocd-secret \ -p '{"stringData": {"oidc.keycloak.clientSecret": "YOUR_CLIENT_SECRET"}}'7. Configure argocd-cm
Edit the argocd-cm ConfigMap:
kubectl -n argocd edit configmap argocd-cmAdd or ensure the data section contains:
apiVersion: v1kind: ConfigMapmetadata: name: argocd-cm namespace: argocddata: url: https://argocd.example.com oidc.config: | name: Keycloak issuer: https://sso.example.com/realms/YOUR_REALM clientID: argocd clientSecret: $oidc.keycloak.clientSecret requestedScopes: - openid - profile - email - groups requestedIDTokenClaims: groups: essential: trueKey notes:
clientSecret: $oidc.keycloak.clientSecret— the$keysyntax tells ArgoCD to read the value from theargocd-secretSecret (stored in step 6), rather than from plain text in the ConfigMap.requestedScopesmust includegroups, matching the client scope created in step 4.
If ArgoCD is managed via a Helm chart (rather than manual kubectl edit), the same configuration can be placed in values.yaml:
configs: cm: url: https://argocd.example.com oidc.config: | name: Keycloak issuer: https://sso.example.com/realms/YOUR_REALM clientID: argocd clientSecret: $oidc.keycloak.clientSecret requestedScopes: ["openid", "profile", "email", "groups"] requestedIDTokenClaims: groups: essential: true8. Configure argocd-rbac-cm
Edit the argocd-rbac-cm ConfigMap to map Keycloak groups to ArgoCD roles:
kubectl -n argocd edit configmap argocd-rbac-cmapiVersion: v1kind: ConfigMapmetadata: name: argocd-rbac-cm namespace: argocddata: policy.default: role:readonly scopes: '[groups]' policy.csv: | # "argocd-admins" group gets full admin access g, argocd-admins, role:admin
# "argocd-developers" group can deploy to non-prod environments p, role:developer, applications, get, */*, allow p, role:developer, applications, sync, dev/*, allow p, role:developer, applications, sync, staging/*, allow p, role:developer, applications, create, dev/*, allow p, role:developer, applications, delete, dev/*, allow g, argocd-developers, role:developer
# "argocd-viewers" group is read-only g, argocd-viewers, role:readonlyImportant notes:
scopes: '[groups]'is required — this tells ArgoCD to read thegroupsclaim from the token when evaluating policies. Without this line, the mappingg, argocd-admins, role:adminwill never match, even if thegroupsclaim is present in the token.policy.default: role:readonly— the default role for users who successfully log in via SSO but don’t belong to any group. You can set this to an empty string ("") if you want users without a group assignment to have no access at all.
9. Restart & Test Login
Apply the changes by restarting argocd-server:
kubectl -n argocd rollout restart deployment argocd-serverWait for the rollout to complete:
kubectl -n argocd rollout status deployment argocd-serverOpen https://argocd.example.com and click the “LOG IN VIA KEYCLOAK” button (the label matches the name defined in oidc.config). After successfully authenticating through Keycloak, verify that you have the expected access level based on the group assigned in step 5.

Optionally, verify the assigned role via the CLI (if the argocd CLI is installed):
argocd login argocd.example.com --ssoargocd account get-user-info10. Break-Glass Account (Local Admin)
ArgoCD ships with a built-in local admin account (its initial password is auto-generated and stored in the argocd-initial-admin-secret Secret). Instead of deleting it, keep it as an emergency access path — similar to the break-glass pattern used in the GitLab integration.
Retrieve the initial password (if you haven’t already):
kubectl -n argocd get secret argocd-initial-admin-secret \ -o jsonpath="{.data.password}" | base64 -dRecommendations:
- Log in once as
admin, change the password to a very strong one, and store it in a password manager. - Do not delete this
adminaccount — keep it active but guard the credentials carefully. Only use it if Keycloak goes down and no OIDC users can log in. - For extra protection, the
adminaccount can be temporarily disabled viaaccounts.admin.enabled: "false"inargocd-cmafter confirming that at least one OIDC group has a workingrole:adminassignment — but make sure you have a way to re-enable it (viakubectl editdirectly against the cluster) before doing this.
11. Offboarding
Unlike GitLab (which requires two separate steps due to account linking), ArgoCD does not store local accounts for OIDC users — access is entirely derived from the groups claim in the token each time a user logs in or re-authenticates. The implications:
- Remove the user from their group in Keycloak (or disable/delete the user) — ArgoCD access is automatically revoked the next time the token or session is refreshed, with no additional steps needed on the ArgoCD side.
- Active sessions will continue to follow the validity of the already-issued token (
requestedIDTokenClaims, Keycloak’s default token expiry). If you need immediate revocation, revoke the user’s session directly from Keycloak (Users → select user → Sessions tab → Sign out), rather than just removing them from the group.
This is one of the key advantages of group-based RBAC over account linking: there are no separate accounts to manually clean up on the ArgoCD side.
12. Troubleshooting
| Symptom | Likely Cause |
|---|---|
| ”LOG IN VIA KEYCLOAK” button doesn’t appear | argocd-cm hasn’t been applied correctly, or argocd-server hasn’t been restarted after the ConfigMap change |
| Login succeeds in Keycloak but ArgoCD shows “permission denied” for all resources | The groups claim isn’t reaching ArgoCD — recheck step 4 (the groups client scope must be Default, and the mapper must have Add to ID token: On), and make sure scopes: '[groups]' is present in argocd-rbac-cm |
| Group matches but the role doesn’t behave as expected | Check the order and syntax of policy.csv — the g, <group>, role:<role> line must exactly match the group name in Keycloak (case-sensitive) |
| Client secret error during login | Ensure the key in the argocd-secret Secret is named exactly oidc.keycloak.clientSecret, matching what’s referenced in oidc.config |
invalid_redirect_uri error from Keycloak | The redirect URI in the Keycloak client must be exactly https://argocd.example.com/auth/callback |
| ConfigMap changes have no effect | argocd-server needs a manual restart after kubectl edit configmap — ArgoCD doesn’t always auto-reload all config sections |
Summary: Create an argocd client in Keycloak with Standard Flow only → add a groups client scope with a Group Membership mapper → create groups (argocd-admins, etc.) and assign users → store the client secret in the Kubernetes argocd-secret Secret → configure oidc.config in argocd-cm → map groups to roles in argocd-rbac-cm with scopes: '[groups]' → restart argocd-server → test login → keep the local admin account as a break-glass path → offboard users by simply removing them from their Keycloak group.