For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.
External OIDC provider
Set up your own OpenID Connect identity provider for single sign-on and role-based access control in the Solo UI.
Replace the built-in auto identity provider with your own OpenID Connect (OIDC) identity provider (IdP), so that users log in to the Solo UI with the credentials that you already manage. The steps in this guide apply to any OIDC-compliant provider, such as Keycloak, Okta, or Microsoft Entra ID.
You create two OIDC clients, because the UI backend and the UI frontend have different security requirements. Then, you map your user groups to Solo UI roles and pass both client IDs to the management Helm chart. For background on the login flow and on role mapping, see Identity providers.
Before you begin
Install the Solo UI, and confirm that you can reach it.
Save the external address of the Solo UI as an environment variable. Your IdP redirects users back to this address after they log in.
export UI_HOST=https://solo-ui.example.comConfirm that your IdP supports the following capabilities. The Solo UI login flow requires all of them.
Capability Use OpenID Connect discovery The Solo UI reads ${BACKEND_ISSUER}/.well-known/openid-configurationto find the authorization, token, and logout endpoints.Authorization code grant The login flow for both clients. Proof Key for Code Exchange (PKCE), with the S256challenge methodThe frontend client, which runs in the browser and holds no client secret. Client credentials grant The backend client, for server-to-server calls. Refresh tokens Renewing an access token without a new login. Custom claims or group mappings in the access token Mapping your user groups to Solo UI roles.
Create the backend client
The UI backend validates access tokens and serves the Solo UI APIs. Create a confidential client for it, because the backend runs server-side and can hold a client secret.
Tip
Create both Solo UI clients in a dedicated realm, tenant, or authorization server, depending on what your IdP calls this grouping. A separate realm keeps the Solo UI users, groups, and token settings apart from your other applications, so that you change the group claim or the token lifetime for the Solo UI alone.
In your IdP, create a client with the following settings.
Setting Value Client type Confidential Grant types Authorization code, client credentials Refresh tokens Enabled Redirect URI ${UI_HOST}/callbackPost-logout redirect URI ${UI_HOST}/logoutNote
The Solo UI serves the callback at the root path by default. If you set
ui.basePathin the management chart to serve the UI under a subpath, add that subpath to both redirect URIs, such as${UI_HOST}/solo/callback.Save the client details as environment variables. You pass these values to the Helm chart later.
BACKEND_CLIENT_ID: The client ID of the backend client.BACKEND_CLIENT_SECRET: The client secret of the backend client. Pass this value in theui.backend.oidc.secretHelm value, and the management chart base64-encodes it and stores it in a Kubernetes secret namedui-backend-oidc-secret. To use your own Kubernetes secret instead, create the secret with the client secret under aclientSecretkey, then setui.backend.oidc.secretRefto the name of your secret and omitui.backend.oidc.secret.BACKEND_ISSUER: The issuer URL of your IdP, such ashttps://keycloak.example.com/realms/solo. The Solo UI appends/.well-known/openid-configurationto this URL to discover the authorization, token, and logout endpoints, so the same issuer serves both the backend and the frontend client.
export BACKEND_CLIENT_ID=<client-id> export BACKEND_CLIENT_SECRET=<client-secret> export BACKEND_ISSUER=<issuer-url>Verify that your IdP serves the discovery document at the issuer URL. The Solo UI fails to start the login flow if this request does not return JSON.
curl -s ${BACKEND_ISSUER}/.well-known/openid-configuration | jq '.authorization_endpoint, .token_endpoint, .end_session_endpoint'
Create the frontend client
The UI frontend runs in the user’s browser. Create a public client for it, so that the login flow uses PKCE instead of a client secret.
In your IdP, create a second client with the following settings.
Setting Value Client type Public Grant types Authorization code with PKCE PKCE challenge method S256. The Solo UI sends no other method.Refresh tokens Enabled Redirect URI ${UI_HOST}/callbackPost-logout redirect URI ${UI_HOST}/logoutSave the client ID as an environment variable.
FRONTEND_CLIENT_ID: The client ID of the frontend client. The frontend client must have Proof Key for Code Exchange (PKCE) enabled with theS256challenge method, such as in a Single-Page Application (SPA) type of IdP application.
The frontend takes no other Helm values. It discovers the authorization, token, and logout endpoints from the same
oidc.issuerURL that the backend uses, so you do not collect those endpoints separately.export FRONTEND_CLIENT_ID=<client-id>If your IdP enforces cross-origin resource sharing (CORS) rules, add
${UI_HOST}as an allowed origin. The browser calls the token endpoint directly, so the IdP blocks the login flow without this entry.
Configure the group claim
The UI backend reads a group claim from the access token and maps those groups to Solo UI roles. Configure your IdP to include the claim; otherwise, no user receives a mapped role.
Create groups in your IdP for each level of access that you want to grant. The default Helm values expect the following three group names.
Group Mapped role Access adminsglobal.AdminFull access to all resources. writersglobal.WriterCreate, read, and update access, but not delete. readersglobal.ReaderRead-only access. Add your users to the groups.
Configure your IdP to return the group memberships in a
Groupsclaim on the access token, not only on the ID token. The UI backend reads the access token. The claim must hold a list of group names, as in the following decoded token.{ "sub": "8a1b...", "email": "maria@example.com", "Groups": ["admins"] }Important
Claim names are case-sensitive, and the default role mapper reads a
Groupsclaim with a capitalG. An IdP that returns a lowercasegroupsclaim, such as Microsoft Entra ID, needs a customrbac.roleMapping.roleMapperexpression. Set that value when you connect the IdP to the Solo UI.Include the claim for the scopes that the Solo UI requests. The UI requests
openid,email, andprofile, plus any scope that you add inoidc.additionalScopes.
Connect the IdP to the Solo UI
Pass your IdP details to the management Helm chart. The relay chart takes no OIDC values, so you upgrade the management release only.
Get the current values of your management Helm release, so that your upgrade preserves them.
helm get values solo-management -n solo-enterprise -o yaml > management.yamlOptional: Create a Kubernetes secret that holds the backend client secret, so that the secret stays out of your Helm values file. The chart reads the value from a
clientSecretkey, and creates no secret of its own when you setui.backend.oidc.secretRefin the next step.kubectl create secret generic my-oidc-secret -n solo-enterprise --from-literal=clientSecret=${BACKEND_CLIENT_SECRET}Add the
oidcand client values tomanagement.yaml. Settingoidc.issuerto a non-empty value turns off the auto identity provider.oidc: issuer: ${BACKEND_ISSUER} ui: backend: oidc: clientId: ${BACKEND_CLIENT_ID} secret: ${BACKEND_CLIENT_SECRET} frontend: oidc: clientId: ${FRONTEND_CLIENT_ID}Field Description oidc.issuerThe issuer URL of your IdP. The Solo UI appends /.well-known/openid-configurationto discover the endpoints. An empty value, which is the default, uses the auto identity provider instead.oidc.additionalScopesScopes to request in addition to openid,email, andprofile. Add the scope here if your IdP returns the group claim only for a custom scope. Defaults to an empty list.ui.backend.oidc.clientIdThe client ID of the backend client. Defaults to kagent-backend, which is a shared-chart default rather than a name to reuse.ui.backend.oidc.secretThe client secret of the backend client. The chart base64-encodes this value and stores it in a Kubernetes secret named ui-backend-oidc-secret.ui.backend.oidc.secretRefThe name of an existing Kubernetes secret that holds the client secret under a clientSecretkey. Use this field instead ofui.backend.oidc.secretto keep the secret out of your Helm values.ui.frontend.oidc.clientIdThe client ID of the frontend client. Defaults to kagent-ui, which is a shared-chart default rather than a name to reuse.Note
If you created a Kubernetes secret in the previous step, replace the
secretfield withsecretRef: my-oidc-secret. Set one field or the other, not both.If your IdP uses a claim name other than
Groups, or group names other thanadmins,writers, andreaders, add the role mapping tomanagement.yaml. Each key inroleMappingsis a group name from your IdP, and each value is the Solo UI role that the members of that group receive. TheroleMapperexpression names the claim that holds the groups. The following example reads a lowercasegroupsclaim, and maps thesolo-admin,solo-writer, andsolo-readergroups to the admin, writer, and reader roles.rbac: roleMapping: roleMapper: "has(claims.groups) ? claims.groups.transformList(i, v, v in rolesMap, rolesMap[v]) : []" roleMappings: solo-admin: "global.Admin" solo-writer: "global.Writer" solo-reader: "global.Reader"Note
Set the mapping in the Helm values rather than editing the
rbac-config-managementConfigMap. The chart regenerates that ConfigMap on every upgrade, which discards a manual edit.Upgrade the management release with your values file.
helm upgrade -i solo-management \ oci://us-docker.pkg.dev/solo-public/solo-enterprise-helm/charts/management \ -n solo-enterprise \ --version 0.5.8 \ --values management.yamlVerify that the
solo-enterprise-uipod restarts and reportsRunning.kubectl get pods -n solo-enterprise -l app=solo-enterprise-uiConfirm that the
idpcontainer is gone from the pod. The management chart deploys the auto identity provider only whileoidc.issueris empty, so its absence confirms that the UI now uses your IdP.kubectl get pod -n solo-enterprise -l app=solo-enterprise-ui -o jsonpath='{.items[0].spec.containers[*].name}'Example output, which no longer includes
idp:tunnel-server ui-frontend ui-backend k8sobjects-collector
Log in to the Solo UI
Open the Solo UI at
${UI_HOST}. Your browser redirects to the login page of your IdP.Log in with the credentials of a user in one of your mapped groups.
Confirm that the user has the access that you expect. A user in an
adminsgroup can edit resources, and a user in areadersgroup cannot.If a mapped user lands with less access than you expect, check the following items in order.
Check How The access token carries the group claim Decode the access token and confirm that the claim name and the group names match your rbac.roleMappingvalues.The claim name matches the role mapper Claim names are case-sensitive. A groupsclaim does not match the default expression, which readsGroups.The ConfigMap holds your mapping Run kubectl get configmap rbac-config-management -n solo-enterprise -o jsonpath='{.data.config}'.Stale browser state is not the cause Log in again in a private browser window. The frontend caches tokens, so a stale token survives a chart upgrade.