Keycloak
Deploy Keycloak as the OIDC identity provider for agentregistry and configure the required clients and realm settings.
Solo Enterprise for agentregistry is protected by an identity provider (IdP) so that only authorized users can access, publish, and deploy AI artifacts. This guide walks through deploying Keycloak as the IdP, creating the required OIDC clients, and configuring the registry server and CLI to use them.
Before you begin
These steps assume that you have a Kubernetes cluster, kubectl, and helm already set up. For quick testing, you can use Kind.
Step 1: Configure Keycloak
Solo Enterprise for agentregistry does not bundle its own identity provider. Instead, it relies on an external OIDC provider to authenticate users, issue access tokens, and enforce role-based access control.
Before you deploy Keycloak, configure it by creating a ConfigMap that defines the realm, clients, protocol mappers, groups, and users that Solo Enterprise for agentregistry requires. Keycloak reads this ConfigMap at startup and automatically creates these resources, so you do not have to configure them manually through the Keycloak UI.
The OIDC client configuration differs depending on the provider you connect to Solo Enterprise for agentregistry.
The following ConfigMap defines the resources for a setup where Solo Enterprise for agentregistry connects to AWS Bedrock AgentCore.
| Resource | Name | Description |
|---|---|---|
| Realm | agentregistry | A dedicated Keycloak realm that isolates agentregistry’s identity configuration from other applications. The registry server uses the realm’s issuer URL to discover OIDC endpoints. |
| Client | ar-backend | A confidential client used by the registry server to validate access tokens issued by the CLI and UI clients. |
| Client | ar-cli-interactive | A public client for the arctl CLI. Uses the device authorization grant (RFC 8628), which lets users log in from a terminal by opening a printed URL in a browser. |
| Client | ar-cli-password | A public client for scripted user authentication in CI/CD pipelines. Uses the password credentials grant with a username and password. |
| Client | ar-ui | A public client for the browser-based registry UI. Uses the authorization code flow with PKCE. The redirectUris and webOrigins are set to * for local testing. In production, replace these with the actual origin your UI uses. |
| Protocol mapper | groups | An oidc-group-membership-mapper that writes the user’s Keycloak group memberships into the Groups JWT claim. The registry reads this claim to determine RBAC roles. Added to access tokens issued for ar-cli-interactive, ar-cli-password, and ar-ui clients. |
| Protocol mapper | ar-backend-audience | An oidc-audience-mapper that adds ar-backend to the aud claim of access tokens. Without the correct aud claim, the registry server rejects tokens with a 401 Unauthorized error. Added to access tokens issued for ar-cli-interactive, ar-cli-password, and ar-ui clients. |
| Group | admins | A group that grants full admin access in the registry. Assigned to admin-user so they have superuser rights on first login. |
| User | admin-user | A test admin user with the password password, assigned to the admins group. |
Create the Keycloak namespace.
kubectl create namespace keycloakCreate the ConfigMap with your Solo Enterprise for agentregistry OIDC configuration.
kubectl apply -f- <<EOF apiVersion: v1 kind: ConfigMap metadata: name: keycloak-agentregistry-realm namespace: keycloak data: agentregistry-realm.json: | { "realm": "agentregistry", "enabled": true, "clients": [ { "clientId": "ar-backend", "enabled": true, "publicClient": false, "standardFlowEnabled": false, "directAccessGrantsEnabled": false }, { "clientId": "ar-cli-interactive", "enabled": true, "publicClient": true, "standardFlowEnabled": false, "directAccessGrantsEnabled": false, "attributes": { "oauth2.device.authorization.grant.enabled": "true" }, "protocolMappers": [ { "name": "groups", "protocol": "openid-connect", "protocolMapper": "oidc-group-membership-mapper", "consentRequired": false, "config": { "claim.name": "Groups", "full.path": "false", "id.token.claim": "true", "access.token.claim": "true", "userinfo.token.claim": "true" } }, { "name": "ar-backend-audience", "protocol": "openid-connect", "protocolMapper": "oidc-audience-mapper", "consentRequired": false, "config": { "included.client.audience": "ar-backend", "id.token.claim": "false", "access.token.claim": "true" } } ] }, { "clientId": "ar-cli-password", "enabled": true, "publicClient": true, "standardFlowEnabled": false, "directAccessGrantsEnabled": true, "protocolMappers": [ { "name": "groups", "protocol": "openid-connect", "protocolMapper": "oidc-group-membership-mapper", "consentRequired": false, "config": { "claim.name": "Groups", "full.path": "false", "id.token.claim": "true", "access.token.claim": "true", "userinfo.token.claim": "true" } }, { "name": "ar-backend-audience", "protocol": "openid-connect", "protocolMapper": "oidc-audience-mapper", "consentRequired": false, "config": { "included.client.audience": "ar-backend", "id.token.claim": "false", "access.token.claim": "true" } } ] }, { "clientId": "ar-ui", "enabled": true, "publicClient": true, "standardFlowEnabled": true, "directAccessGrantsEnabled": false, "redirectUris": ["*"], "webOrigins": ["*"], "attributes": { "pkce.code.challenge.method": "S256" }, "protocolMappers": [ { "name": "groups", "protocol": "openid-connect", "protocolMapper": "oidc-group-membership-mapper", "consentRequired": false, "config": { "claim.name": "Groups", "full.path": "false", "id.token.claim": "true", "access.token.claim": "true", "userinfo.token.claim": "true" } }, { "name": "ar-backend-audience", "protocol": "openid-connect", "protocolMapper": "oidc-audience-mapper", "consentRequired": false, "config": { "included.client.audience": "ar-backend", "id.token.claim": "false", "access.token.claim": "true" } } ] } ], "groups": [ {"name": "admins"} ], "users": [ { "username": "admin-user", "email": "admin-user@example.com", "firstName": "Admin", "lastName": "User", "enabled": true, "credentials": [ {"type": "password", "value": "password", "temporary": false} ], "groups": ["admins"] } ] } EOF
The following ConfigMap defines the resources for a setup where Solo Enterprise for agentregistry connects to Solo Enterprise for kagent. This configuration includes additional clients for the kagent management plane UI.
| Resource | Name | Description |
|---|---|---|
| Realm | agentregistry | A dedicated Keycloak realm that isolates agentregistry’s identity configuration from other applications. The registry server uses the realm’s issuer URL to discover OIDC endpoints. |
| Client | ar-backend | A confidential client used by the registry server to validate access tokens issued by the CLI and UI clients. |
| Client | ar-cli-interactive | A public client for the arctl CLI. Uses the device authorization grant (RFC 8628), which lets users log in from a terminal by opening a printed URL in a browser. |
| Client | ar-cli-password | A public client for scripted user authentication in CI/CD pipelines. Uses the password credentials grant with a username and password. |
| Client | ar-ui | A public client for the browser-based registry UI. Uses the authorization code flow with PKCE. The redirectUris and webOrigins are set to * for local testing. In production, replace these with the actual origin your UI uses. |
| Client | kagent-backend | A confidential client used by the kagent-enterprise controller and the solo-enterprise-ui backend to validate access tokens issued to kagent-ui. Tokens that include ar-backend in their aud claim are not accepted — only tokens with kagent-backend in the audience are. |
| Client | kagent-ui | A public client for the solo-enterprise-ui browser frontend. Uses the authorization code flow with PKCE. |
| Protocol mapper | groups | An oidc-group-membership-mapper that writes the user’s Keycloak group memberships into the Groups JWT claim. Added to access tokens issued for ar-cli-interactive, ar-cli-password, ar-ui, and kagent-ui clients. |
| Protocol mapper | ar-backend-audience | An oidc-audience-mapper that adds ar-backend to the aud claim of access tokens issued for ar-cli-interactive, ar-cli-password, and ar-ui clients. Without the correct aud claim, the registry server rejects tokens with a 401 Unauthorized error. |
| Protocol mapper | kagent-backend-audience | An oidc-audience-mapper that adds kagent-backend to the aud claim of access tokens issued for the kagent-ui client. Without this, kagent-enterprise rejects tokens from the UI with a 401 Unauthorized error. |
| Group | admins | A group that grants full admin access in the registry. Assigned to admin-user so they have superuser rights on first login. |
| User | admin-user | A test admin user with the password password, assigned to the admins group. |
Create the Keycloak namespace.
kubectl create namespace keycloakCreate the ConfigMap with your Solo Enterprise for agentregistry OIDC configuration.
kubectl apply -f- <<EOF apiVersion: v1 kind: ConfigMap metadata: name: keycloak-agentregistry-realm namespace: keycloak data: agentregistry-realm.json: | { "realm": "agentregistry", "enabled": true, "clients": [ { "clientId": "ar-backend", "enabled": true, "publicClient": false, "standardFlowEnabled": false, "directAccessGrantsEnabled": false }, { "clientId": "ar-cli-interactive", "enabled": true, "publicClient": true, "standardFlowEnabled": false, "directAccessGrantsEnabled": false, "attributes": { "oauth2.device.authorization.grant.enabled": "true" }, "protocolMappers": [ { "name": "groups", "protocol": "openid-connect", "protocolMapper": "oidc-group-membership-mapper", "consentRequired": false, "config": { "claim.name": "Groups", "full.path": "false", "id.token.claim": "true", "access.token.claim": "true", "userinfo.token.claim": "true" } }, { "name": "ar-backend-audience", "protocol": "openid-connect", "protocolMapper": "oidc-audience-mapper", "consentRequired": false, "config": { "included.client.audience": "ar-backend", "id.token.claim": "false", "access.token.claim": "true" } } ] }, { "clientId": "ar-cli-password", "enabled": true, "publicClient": true, "standardFlowEnabled": false, "directAccessGrantsEnabled": true, "protocolMappers": [ { "name": "groups", "protocol": "openid-connect", "protocolMapper": "oidc-group-membership-mapper", "consentRequired": false, "config": { "claim.name": "Groups", "full.path": "false", "id.token.claim": "true", "access.token.claim": "true", "userinfo.token.claim": "true" } }, { "name": "ar-backend-audience", "protocol": "openid-connect", "protocolMapper": "oidc-audience-mapper", "consentRequired": false, "config": { "included.client.audience": "ar-backend", "id.token.claim": "false", "access.token.claim": "true" } } ] }, { "clientId": "ar-ui", "enabled": true, "publicClient": true, "standardFlowEnabled": true, "directAccessGrantsEnabled": false, "redirectUris": ["*"], "webOrigins": ["*"], "attributes": { "pkce.code.challenge.method": "S256" }, "protocolMappers": [ { "name": "groups", "protocol": "openid-connect", "protocolMapper": "oidc-group-membership-mapper", "consentRequired": false, "config": { "claim.name": "Groups", "full.path": "false", "id.token.claim": "true", "access.token.claim": "true", "userinfo.token.claim": "true" } }, { "name": "ar-backend-audience", "protocol": "openid-connect", "protocolMapper": "oidc-audience-mapper", "consentRequired": false, "config": { "included.client.audience": "ar-backend", "id.token.claim": "false", "access.token.claim": "true" } } ] }, { "clientId": "kagent-backend", "enabled": true, "publicClient": false, "standardFlowEnabled": false, "directAccessGrantsEnabled": false }, { "clientId": "kagent-ui", "enabled": true, "publicClient": true, "standardFlowEnabled": true, "directAccessGrantsEnabled": false, "redirectUris": ["*"], "webOrigins": ["*"], "attributes": { "pkce.code.challenge.method": "S256" }, "protocolMappers": [ { "name": "groups", "protocol": "openid-connect", "protocolMapper": "oidc-group-membership-mapper", "consentRequired": false, "config": { "claim.name": "Groups", "full.path": "false", "id.token.claim": "true", "access.token.claim": "true", "userinfo.token.claim": "true" } }, { "name": "kagent-backend-audience", "protocol": "openid-connect", "protocolMapper": "oidc-audience-mapper", "consentRequired": false, "config": { "included.client.audience": "kagent-backend", "id.token.claim": "false", "access.token.claim": "true" } } ] } ], "groups": [ {"name": "admins"} ], "users": [ { "username": "admin-user", "email": "admin-user@example.com", "firstName": "Admin", "lastName": "User", "enabled": true, "credentials": [ {"type": "password", "value": "password", "temporary": false} ], "groups": ["admins"] } ] } EOF
Step 2: Deploy Keycloak
With the realm configuration in place, deploy Keycloak. This step creates the Keycloak pod and exposes it through a load balancer service so that both the registry server and the arctl CLI can reach it. The --import-realm flag instructs Keycloak to load the ConfigMap from Step 1 on startup to apply all the realm, client, and user configuration automatically.
Create the Keycloak deployment and service.
kubectl apply -f- <<EOF apiVersion: v1 kind: Service metadata: name: keycloak namespace: keycloak labels: app: keycloak spec: ports: - name: http port: 8080 targetPort: 8080 selector: app: keycloak type: LoadBalancer --- apiVersion: apps/v1 kind: Deployment metadata: name: keycloak namespace: keycloak labels: app: keycloak spec: replicas: 1 selector: matchLabels: app: keycloak template: metadata: labels: app: keycloak spec: containers: - name: keycloak image: quay.io/keycloak/keycloak:26.1.3 args: ["start-dev", "--import-realm"] env: - name: KEYCLOAK_ADMIN value: "admin" - name: KEYCLOAK_ADMIN_PASSWORD value: "admin" - name: PROXY_ADDRESS_FORWARDING value: "true" - name: KC_PROXY value: "edge" ports: - name: http containerPort: 8080 readinessProbe: httpGet: path: /realms/master port: 8080 volumeMounts: - name: realm-config mountPath: /opt/keycloak/data/import volumes: - name: realm-config configMap: name: keycloak-agentregistry-realm EOFWait for the Keycloak rollout to finish.
kubectl -n keycloak rollout status deploy/keycloakSet the Keycloak endpoint details from the load balancer service. These environment variables are used throughout the rest of this guide. The
KEYCLOAK_ISSUERis the OIDC discovery URL that the registry uses to find the token and JWKS endpoints. If you are running locally in Kind and need a local IP address for the load balancer service, consider using cloud-provider-kind.export ENDPOINT_KEYCLOAK=$(kubectl -n keycloak get service keycloak -o jsonpath='{.status.loadBalancer.ingress[0].ip}{.status.loadBalancer.ingress[0].hostname}'):8080 export HOST_KEYCLOAK=$(echo ${ENDPOINT_KEYCLOAK} | cut -d: -f1) export PORT_KEYCLOAK=$(echo ${ENDPOINT_KEYCLOAK} | cut -d: -f2) export KEYCLOAK_URL=http://${ENDPOINT_KEYCLOAK} export KEYCLOAK_REALM=agentregistry export KEYCLOAK_ISSUER=$KEYCLOAK_URL/realms/${KEYCLOAK_REALM} echo $KEYCLOAK_ISSUERGet the client secrets that Keycloak automatically generated. You pass these secrets to the respective Helm charts in the next steps.
Get the
ar-backendclient secret. The registry server uses this secret to verify access tokens.export KEYCLOAK_TOKEN=$(curl -s \ -d "client_id=admin-cli" -d "username=admin" -d "password=admin" \ -d "grant_type=password" \ "${KEYCLOAK_URL}/realms/master/protocol/openid-connect/token" \ | jq -r .access_token) export AR_BACKEND_ID=$(curl -s -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" \ "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/clients?clientId=ar-backend" \ | jq -r '.[0].id') export AR_BACKEND_SECRET=$(curl -s -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" \ "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/clients/${AR_BACKEND_ID}/client-secret" \ | jq -r .value) echo "AR_BACKEND_SECRET: $AR_BACKEND_SECRET"Get the
ar-backendandkagent-backendclient secrets. The registry server uses thear-backendsecret to verify access tokens. Thekagent-enterprisecontroller uses thekagent-backendsecret to verify tokens that are issued to thekagent-uiclient.export KEYCLOAK_TOKEN=$(curl -s \ -d "client_id=admin-cli" -d "username=admin" -d "password=admin" \ -d "grant_type=password" \ "${KEYCLOAK_URL}/realms/master/protocol/openid-connect/token" \ | jq -r .access_token) export AR_BACKEND_ID=$(curl -s -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" \ "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/clients?clientId=ar-backend" \ | jq -r '.[0].id') export AR_BACKEND_SECRET=$(curl -s -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" \ "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/clients/${AR_BACKEND_ID}/client-secret" \ | jq -r .value) echo "AR_BACKEND_SECRET: $AR_BACKEND_SECRET" export KAGENT_BACKEND_ID=$(curl -s -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" \ "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/clients?clientId=kagent-backend" \ | jq -r '.[0].id') export KAGENT_BACKEND_SECRET=$(curl -s -H "Authorization: Bearer ${KEYCLOAK_TOKEN}" \ "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/clients/${KAGENT_BACKEND_ID}/client-secret" \ | jq -r .value) echo "KAGENT_BACKEND_SECRET: $KAGENT_BACKEND_SECRET"
Step 3: Install Solo Enterprise for kagent
Save your Solo Enterprise for kagent license key in an environment variable. To obtain the key, contact an account representative. If you prefer to specify the license key in a secret instead, see Licensing.
export KAGENT_LICENSE_KEY="<key>"Set the Solo Enterprise for kagent patch version. You can find available versions in the reference documentation.
export KAGENT_ENT_VERSION=0.3.17Install the
kagent-mgmtchart for Solo Enterprise for kagent.helm upgrade -i kagent-mgmt \ oci://us-docker.pkg.dev/solo-public/solo-enterprise-helm/charts/management \ -n kagent --create-namespace \ --version "$KAGENT_ENT_VERSION" \ --set cluster=mgmt-cluster \ --set products.kagent.enabled=true \ --set products.agentregistry.enabled=true \ --set-string licensing.licenseKey="$KAGENT_LICENSE_KEY" \ --set-string oidc.issuer="$KEYCLOAK_ISSUER" \ --set-string ui.backend.oidc.clientId=kagent-backend \ --set-string ui.backend.oidc.secret="$KAGENT_BACKEND_SECRET" \ --set-string ui.frontend.oidc.clientId=kagent-uiVerify that the management component pods have a status of
Running.kubectl get po -n kagentExample output:
NAME READY STATUS RESTARTS AGE kagent-mgmt-clickhouse-shard0-0 1/1 Running 0 4s solo-enterprise-telemetry-collector-0 1/1 Running 0 4s solo-enterprise-ui-76c85c7d6b-xftcj 5/5 Running 0 4sInstall the kagent-enterprise CRDs chart.
helm upgrade --install kagent-crds \ oci://us-docker.pkg.dev/solo-public/kagent-enterprise-helm/charts/kagent-enterprise-crds \ -n kagent \ --version "$KAGENT_ENT_VERSION"Install the kagent-enterprise chart.
helm upgrade -i kagent \ oci://us-docker.pkg.dev/solo-public/kagent-enterprise-helm/charts/kagent-enterprise \ -n kagent \ --version "$KAGENT_ENT_VERSION" \ --set-string licensing.licenseKey="$KAGENT_LICENSE_KEY" \ --set agents.k8s-agent.enabled=true \ --set kagent-tools.enabled=true \ --set kmcp.licensing.createSecret=false \ --set-string oidc.issuer="$KEYCLOAK_ISSUER" \ --set oidc.clientId=kagent-backend \ --set-string oidc.secret="$KAGENT_BACKEND_SECRET"Verify that all pods are running.
kubectl get po -n kagentExample output:
kagent-controller-dfc4c966c-zzds5 1/1 Running 0 3m kagent-mgmt-clickhouse-shard0-0 1/1 Running 0 5m48s kagent-postgresql-57c4f5597f-x4mjg 1/1 Running 0 3m kagent-tools-69bdb559df-cb9bw 1/1 Running 0 3m kmcp-enterprise-controller-manager-dbd59bcbf-j5hjt 1/1 Running 0 3m solo-enterprise-telemetry-collector-0 1/1 Running 0 5m48s solo-enterprise-ui-66cbb6687c-6czzk 4/4 Running 0 5m48s
Step 4: Install Solo Enterprise for agentregistry
Install Solo Enterprise for agentregistry and connect it to the Keycloak instance that you set up. The OIDC Helm values point the registry server at your Keycloak realm so that it knows what endpoint to use to validate access tokens, which client credentials to use, and which JWT claims carry the role information used for RBAC decisions.
Save your Solo Enterprise for agentregistry license key in an environment variable, then install Solo Enterprise for agentregistry. To obtain the key, contact an account representative. For more options, such as providing the key in a secret, see Licensing.
export AGENTREGISTRY_LICENSE_KEY=<license_key>helm upgrade --install agentregistry \ oci://us-docker.pkg.dev/solo-public/agentregistry-enterprise/helm/agentregistry-enterprise \ --version 2026.7.0 \ --namespace agentregistry-system \ --create-namespace \ --set licensing.createSecret=true \ --set licensing.licenseKey=$AGENTREGISTRY_LICENSE_KEY \ --set oidc.issuer=$KEYCLOAK_ISSUER \ --set oidc.clientId=ar-backend \ --set oidc.clientSecret=$AR_BACKEND_SECRET \ --set oidc.publicClientId=ar-ui \ --set oidc.roleClaim=Groups \ --set oidc.superuserRole=adminsHelm value Description licensing.createSecretSet to trueso that the chart creates the license secret from thelicensing.licenseKeyvalue.licensing.licenseKeyYour Solo Enterprise for agentregistry license key. oidc.issuerThe issuer URL of your Keycloak realm. The registry uses this URL to discover the OIDC provider’s endpoints. oidc.clientIdThe confidential backend client ID ( ar-backend).oidc.clientSecretThe client secret for the ar-backendclient.oidc.publicClientIdThe public client ID for the browser-based UI ( ar-ui). The server uses this client to log in to the UI.oidc.roleClaimThe JWT claim that contains role or group names. Must match the Token Claim Name in the group mapper ( Groups).oidc.superuserRoleThe group name that grants full admin access ( admins).Verify that the Solo Enterprise for agentregistry pods are up and running.
kubectl get pods -n agentregistry-systemExample output:
NAME READY STATUS RESTARTS AGE agentregistry-clickhouse-shard0-0 1/1 Running 0 40s agentregistry-enterprise-postgresql-764898cbff-hwb4r 1/1 Running 0 40s agentregistry-enterprise-server-79c4cdcdf8-dp6h4 1/1 Running 0 40s agentregistry-enterprise-telemetry-collector-777798c5b4-rctfs 1/1 Running 0 40sPort-forward the Solo Enterprise for agentregistry controller on port 12121. The registry service is not exposed externally, so port-forwarding is required to reach it from your local machine. The
arctlCLI and the UI both default tohttp://localhost:12121as the registry endpoint.kubectl -n agentregistry-system port-forward svc/agentregistry-enterprise-server 12121:12121
Step 5: Log in with the CLI
Download and install the
arctlCLI.curl -sSL https://storage.googleapis.com/agentregistry-enterprise/install.sh | ARCTL_VERSION=v2026.7.0 sh export PATH=$HOME/.arctl/bin:$PATHIf you previously used the registry in demo auth mode, unset the
ARCTL_API_TOKENenvironment variable. This step is required to remove any previously obtained access tokens so that requests are not rejected with a 401 HTTP response.unset ARCTL_API_TOKENLog in to the registry. Choose between the interactive device authorization and the static username and password credential flow.
Use this flow for interactive logins from a terminal. The CLI uses the
ar-cli-interactivepublic client and starts a device authorization grant flow that prints a URL and a one-time code for you to approve in a browser.Configure the CLI to use the Keycloak issuer and
ar-cli-interactiveclient. Then, log in to Solo Enterprise for agentregistry.export OIDC_ISSUER=$KEYCLOAK_ISSUER export OIDC_CLIENT_ID=ar-cli-interactive arctl user loginExample output:
To complete the login process, please: 1. Open: http://172.18.0.13:8080/realms/agentregistry/device 2. Enter the code: SYSQ-SNSP - or go to: http://172.18.0.13:8080/realms/agentregistry/device?user_code=SYSQ-SNSPOpen the URL in a web browser and log into Keycloak with the
admin-userusername andpasswordpassword. Confirming the code in the browser tells Keycloak to issue an access token for the CLI session. After authorization is complete, the CLI stores the token in your system keychain so you do not have to log in again until it expires.
Use this flow for scripted logins where a specific user identity is required in CI/CD pipelines. The CLI uses the
ar-cli-passwordpublic client and exchanges a username and password directly for an access token. The password can be supplied with the--oidc-passwordflag or theOIDC_CLIENT_PASSWORDenvironment variable. If both are set, the CLI flag takes precedence.Log in to Solo Enterprise for agentregistry with the
admin-user/passwordcredentials.export OIDC_ISSUER=$KEYCLOAK_ISSUER arctl user login \ --oidc-flow=password-credentials \ --oidc-client-id=ar-cli-password \ --oidc-username=admin-user \ --oidc-password=passwordSave the access token in the
ARCTL_API_TOKENenvironment variable.export ARCTL_API_TOKEN=$(arctl user info --show-tokens | jq -r .access_token)
Verify that you can run
arctlcommands.arctl user whoamiExample output:
FIELD VALUE Subject 37aae242-96df-4261-81c6-2b8fd8c4e8a8 Email admin-user@example.com Issuer http://172.18.0.17:8080/realms/agentregistry Superuser true (overrides role-based permission checks) ROLE STATUS admins no explicit permissions configured
401 errors in the CLI, you might have previously stored a token in the ARCTL_API_TOKEN environment variable. This variable takes precedence over the token obtained via the CLI login flow. Run unset ARCTL_API_TOKEN and repeat the login flow.Cleanup
kubectl delete namespace keycloak