Configure Dex to Use Keycloak as an OIDC Connector
PaletteAI authenticates users through Dex, which can broker authentication to an external OpenID Connect (OIDC) provider. This page shows how to configure Dex to use Keycloak as an OIDC connector so that PaletteAI users sign in with their Keycloak credentials.
The authentication flow is: user -> PaletteAI -> Dex -> Keycloak -> Dex -> PaletteAI.
Dex can connect to many OIDC providers — Keycloak is one example. For the full list and per-provider configuration, refer to the upstream Dex connectors reference.
Prerequisites
-
A reachable Keycloak instance with administrator access. If you need to deploy one, refer to the upstream Keycloak server installation guide.
-
A running PaletteAI installation. If you have not installed PaletteAI yet, complete the Self-Hosted Quickstart or one of the following installation guides: Vanilla Kubernetes, AWS EKS, AWS IaaS, or GKE.
This page only configures user sign-in through Keycloak. To control what each authenticated user can do in the cluster, follow either Configure Kubernetes API Server to Trust OIDC Provider or Configure User Impersonation to enforce per-user Role-Based Access Control (RBAC).
The two enforcement paths are mutually exclusive. If you configure the Kubernetes API server to trust Dex, do not also enable user impersonation. Refer to Configure User Impersonation, which documents this restriction in detail.
Enablement
Configure the Keycloak Realm and OIDC Client
-
Sign in to the Keycloak admin console and create a new realm named
paletteai. Do not configure the integration in the default Keycloak administration realm. -
In the realm, create the groups that you want to map to Kubernetes groups. This page uses
admins,editors, andviewersas a worked example. -
Create users and assign each user to the appropriate group. For each user, set a non-temporary password under the Credentials tab and turn on Email verified.
-
Create an OIDC client for Dex:
-
Set Client type to OpenID Connect and Client ID to
dex. -
Turn on Client authentication.
-
Enable only the Standard flow capability.
-
Set Valid redirect URIs to
https://<your-paletteai-domain>/dex/callback. -
Set Valid post logout redirect URIs and Web origins to
+(Keycloak shorthand for "use the same values as Valid Redirect URIs").
-
-
Copy the value from the Credentials tab of the
dexclient. This value becomes theclientSecretin the Dex connector configuration. -
Create a client scope named
groups:-
Set Protocol to OpenID Connect and Type to Default.
-
Leave Display on consent screen turned off.
-
-
Assign the
groupsscope to thedexclient as a default scope. -
Open the
dex-dedicatedclient scope (Keycloak auto-creates this scope for every client) and add a Group Membership mapper with the following values:-
Name:
groups -
Token Claim Name:
member_groups -
Full group path: off
-
Add to ID token: on
-
Add to access token: on
-
Add to
userinfo: on
infoDex does not pick up a claim named
groupsfrom upstream OIDC providers reliably. Emit the claim asmember_groupsin Keycloak and map it back togroupsin the Dex connector configuration (Configure the Dex Connector). -
-
Verify the Keycloak configuration by requesting a token and inspecting the
userinfoendpoint.TOKEN=$(curl --silent --request POST \
"https://<your-keycloak-domain>/realms/paletteai/protocol/openid-connect/token" \
--data "client_id=dex" \
--data "client_secret=<client-secret>" \
--data "grant_type=password" \
--data "username=<user-email>" \
--data "password=<user-password>" \
--data "scope=openid profile email groups" | jq --raw-output '.access_token')
curl --silent --header "Authorization: Bearer $TOKEN" \
"https://<your-keycloak-domain>/realms/paletteai/protocol/openid-connect/userinfo" | jq .The response includes a
member_groupsarray.Example Output{
"sub": "...",
"email_verified": true,
"name": "Jane Doe",
"member_groups": ["admins"],
"preferred_username": "jane@example.com",
"email": "jane@example.com"
}
Configure the Dex Connector
Add a Keycloak connector to your PaletteAI Helm values.yaml under dex.config.connectors.
dex:
config:
connectors:
- type: oidc
id: keycloak
name: Keycloak
config:
issuer: https://<your-keycloak-domain>/realms/paletteai
clientID: dex
clientSecret: <client-secret-from-keycloak>
redirectURI: https://<your-paletteai-domain>/dex/callback
getUserInfo: true
insecureEnableGroups: true
insecureSkipEmailVerified: true
scopes:
- profile
- email
- groups
claimMapping:
groups: member_groups
Each of the following fields is required for the Keycloak integration to work:
-
getUserInfo: trueforces Dex to fetch claims from theuserinfoendpoint. Without this, Keycloak group information may not reach Dex. -
insecureEnableGroups: trueis required for Dex to read and forward group claims from upstream OIDC providers. Theinsecureprefix is historical — it dates from when the feature was experimental, not from any security concern. -
insecureSkipEmailVerified: trueprevents Dex from rejecting users whose email verification status it cannot confirm from the upstream provider. -
claimMapping.groups: member_groupsmaps themember_groupsclaim from Keycloak to Dex's internalgroupsfield. This works around the Dex behavior noted in the Group Membership mapper step. -
redirectURImust use the public PaletteAI URL: Keycloak redirects the user's browser to this URL after authentication.
Your existing Dex static clients and static passwords remain unchanged. The Keycloak connector coexists with local Dex users — at the sign-in page, users can either enter credentials for a local Dex user or select the Keycloak connector.
Configure PaletteAI for OIDC
In the same values.yaml, configure the PaletteAI OIDC settings.
canvas:
oidc:
sessionSecret: '' # leave empty; populated via global.auth.sessionSecret
sessionDir: '/app/sessions'
issuerK8sService: '' # leave empty; automatically configured in the Helm chart templates
skipSSLCertificateVerification: false
redirectURL: 'https://<your-paletteai-domain>/ai/callback'
Set skipSSLCertificateVerification to true only when Dex presents a self-signed certificate that the PaletteAI UI cannot otherwise trust.
Configure User Impersonation
Skip this subsection if you are configuring the Kubernetes API server to trust Dex directly. If you are using user impersonation instead, add a canvas.impersonationProxy block that maps the Keycloak groups created in Configure the Keycloak Realm and OIDC Client to Kubernetes groups. The minimal configuration below maps admins, editors, and viewers to the built-in Kubernetes admin, edit, and view ClusterRole resources.
canvas:
impersonationProxy:
enabled: true
userMode: passthrough
groupsMode: map
userMap: {}
groupMap:
admins: ['admin']
editors: ['edit']
viewers: ['view']
dexGroupMap: {}
groupsMode: map is required when you also need to assign Kubernetes groups to local Dex users through dexGroupMap. groupsMode: passthrough cannot be combined with dexGroupMap.
Refer to Configure User Impersonation for the full parameter reference and additional mapping scenarios.
Apply the Configuration
Apply the changes with Helm.
helm upgrade mural oci://public.ecr.aws/mural/mural \
--namespace <namespace> \
--version <version> \
--values <values-file> \
--atomic --timeout 5m
After the upgrade completes, restart Dex and the PaletteAI UI so they pick up the new configuration.
kubectl rollout restart deployment dex --namespace <namespace>
kubectl rollout restart deployment canvas --namespace <namespace>
Validate
-
Sign in to PaletteAI. Your browser is redirected to Dex, then to Keycloak, and finally back to PaletteAI, where you are signed in.
-
Confirm that Dex received the groups from Keycloak.
kubectl logs --namespace <namespace> --selector app.kubernetes.io/name=dex --tail=20Look for a line that contains
groups=[admins](or your own group names). An entry ofgroups=[]indicates that the connector did not receive the claim. Refer to Common Issues. -
Verify that Kubernetes RBAC grants the expected permissions for each group. Substitute the appropriate user email and Kubernetes group name for each probe. The trailing comment on each line shows the expected result.
kubectl auth can-i create pods --as=admin@example.com --as-group=admin # yes
kubectl auth can-i create pods --as=editor@example.com --as-group=edit # yes
kubectl auth can-i create clusterrolebindings --as=editor@example.com --as-group=edit # no
kubectl auth can-i get pods --as=viewer@example.com --as-group=view # yes
kubectl auth can-i create pods --as=viewer@example.com --as-group=view # noThe two
noresults confirm that the boundClusterRoleresources scope down to the expected permissions (editcannot create cluster-scoped bindings,viewis read-only).
Common Issues
Invalid Redirect URI from Keycloak
The redirectURI in the Dex connector configuration must exactly match a Valid Redirect URIs entry on the dex client in Keycloak. Differences as small as a trailing slash cause this error. Inspect the redirect_uri query parameter in the browser address bar to confirm the exact value Dex is sending.
Invalid Scopes Error
The groups scope must exist as a client scope in Keycloak and be assigned as a default scope to the dex client. Refer to the client-scope steps in Configure the Keycloak Realm and OIDC Client.
Empty Groups in Dex Logs
When groups=[] appears in the Dex logs after a successful sign-in, the cause is typically one of the following:
-
insecureEnableGroups: trueis missing from the Dex connector configuration. -
The Group Membership mapper is not configured on the
dex-dedicatedclient scope in Keycloak. -
The user is not assigned to any group in Keycloak.
No Impersonation Groups Found
Groups are reaching Dex but not PaletteAI. Confirm that canvas.impersonationProxy.groupsMode is set to map and that every external group is listed under groupMap. For local Dex users, confirm that each user is listed under dexGroupMap.
Next Steps
Dex now authenticates users through Keycloak. To finish, choose how Kubernetes enforces per-user RBAC:
-
Configure Kubernetes API Server to Trust OIDC Provider — for clusters where you control the Kubernetes API server flags.
-
Configure User Impersonation — for managed clusters, or when you also need to grant permissions to local Dex users through
dexGroupMap.
Until the Kubernetes API server is configured to use Dex as its OIDC provider, Kubernetes RBAC enforcement is UI-only. Any user with a valid kubeconfig bypasses OIDC group enforcement and is authorized solely by the credentials in that kubeconfig.
To use a different identity provider, replace the keycloak connector in dex.config.connectors. Refer to the upstream Dex connectors reference for all supported connectors and their configuration options.