For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.
Secure the UI
Require users to log in to the UI with an OIDC policy on the gateway that serves it.
About
To require users to authenticate, apply a browser OIDC policy to the gateway that serves the UI. Unauthenticated requests are redirected to your identity provider (IdP) to log in, and only requests that pass the policies in ui.policies reach the UI.
The ui.policies section takes the same policies that a route takes, so you can also use JWT, basic, or API key authentication for programmatic access. To restrict which authenticated users get in, add an authorization policy alongside the authentication policy.
Note
A policy in ui.policies applies only to the gateways that the ui section lists. It does not apply to the copy of the UI on the admin interface, which stays unauthenticated. That is safe by default, because the admin address is loopback-only, but it is a reason not to move it. For more information, see The UI and the admin interface are not the same thing.
Before you begin
- Install standalone agentgateway.
- Serve the UI on its own gateway. The examples on this page apply the OIDC policy to the
ui-gatewayon port4001that you created in that guide. - Set up an IdP, such as Keycloak or Microsoft Entra ID. Consider creating a client specifically for the UI, such as
agentgateway-ui. For provider-specific setup instructions, see the identity provider integrations.
Binary and Docker
Set the
OIDC_COOKIE_SECRETenvironment variable. Agentgateway requires this value to encrypt session cookies whenever anoidcpolicy is configured, and refuses to start without it. The key is an AES-256-GCM key, which is 32 random bytes encoded as 64 hexadecimal characters. It is a random value that you generate, not a value that your identity provider gives you.export OIDC_COOKIE_SECRET="$(openssl rand -hex 32)"Save the details of the UI client that you created in your IdP as environment variables. The redirect URI must match the address that you serve the UI on, and it must be registered as a valid redirect URI in your IdP. The following example uses a local address so that you can test the login flow first. When you expose the UI on a hostname, change this value to that hostname and register it in your IdP.
export ISSUER_URL=https://keycloak.example.com/realms/agentgateway export UI_CLIENT_ID=agentgateway-ui export UI_CLIENT_SECRET=<client-secret> export REDIRECT_URI=http://localhost:4001/oauth/callbackAdd an
oidcpolicy to theuisection of your configuration file. The following example redirects unauthenticated users on theui-gatewayto the OIDC provider to log in. The optionalauthorizationpolicy further restricts access to users whose email address ends in@example.com.Agentgateway expands environment variables in the configuration file when it loads the file, so you can refer to the values that you exported in the previous step instead of writing the client secret into the file.
# yaml-language-server: $schema=https://agentgateway.dev/schema/config gateways: default: port: 4000 ui-gateway: port: 4001 ui: gateways: [ui-gateway] policies: oidc: issuer: ${ISSUER_URL} clientId: ${UI_CLIENT_ID} clientSecret: ${UI_CLIENT_SECRET} redirectURI: ${REDIRECT_URI} scopes: - profile - email authorization: rules: - allow: jwt.email.endsWith("@example.com")Tip
For the full list of
oidcpolicy fields and a complete runnable Keycloak setup, see OIDC browser authentication and thetraffic-unified-gatewayexample in the agentgateway repository.Start agentgateway with the updated config.
agentgateway -f config.yamlOpen the UI at the gateway’s address, such as http://localhost:4001/ui/. Instead of loading the UI directly, agentgateway redirects you to the OIDC provider to log in. After you authenticate, you are returned to the UI.
Helm
Save the details of the UI client that you created in your IdP as environment variables. The redirect URI must match the address that you expose the UI on, and it must be registered as a valid redirect URI in your IdP. If you plan to expose the UI on a hostname, use that hostname now so that you do not have to register a second redirect URI later.
export ISSUER_URL=https://keycloak.example.com/realms/agentgateway export UI_CLIENT_ID=agentgateway-ui export UI_CLIENT_SECRET=<client-secret> export REDIRECT_URI=https://agentgateway.example.com/oauth/callbackCreate a Secret that holds the session cookie encryption key and the OIDC client secret.
After a user logs in, agentgateway keeps the session in a browser cookie that it encrypts with the session cookie encryption key. The key is a random value that you generate, not a value that your IdP gives you. Agentgateway requires an AES-256-GCM key, which is 32 random bytes that are encoded as 64 hexadecimal characters. Agentgateway refuses to start when an
oidcpolicy is set and this key is missing or is not that length.kubectl create secret generic agentgateway-ui-secrets \ -n agentgateway-system \ --from-literal=OIDC_COOKIE_SECRET="$(openssl rand -hex 32)" \ --from-literal=UI_CLIENT_SECRET="${UI_CLIENT_SECRET}"Note
You choose the Secret’s name, and you pass it to the chart in the
oidc.cookieSecretNamevalue in the next step. However, the key within the Secret must be namedOIDC_COOKIE_SECRET, because the chart reads that exact key. The client secret key can have any name, as long as theextraEnventry that you add in the next step refers to the same name. This example keeps both values in one Secret, but you can also keep them in separate Secrets. In that case, setoidc.cookieSecretNameto the Secret that holds the cookie key, and point theextraEnventry at the Secret that holds the client secret.Add the OIDC policy to the
uisection, point the chart at the Secret, and pass the client secret to the pod as an environment variable.The heredoc in this step is unquoted, so your shell substitutes the issuer, client ID, and redirect URI as it writes the file. The
\$UI_CLIENT_SECRETreference is escaped, so it stays in the file as a literal$UI_CLIENT_SECRETthat agentgateway resolves from the pod environment at startup. This way, the client secret stays in the Secret instead of the ConfigMap.cat <<EOF > values.yaml config: gateways: default: port: 4000 ui-gateway: port: 4001 ui: gateways: [ui-gateway] policies: oidc: issuer: ${ISSUER_URL} clientId: ${UI_CLIENT_ID} clientSecret: \$UI_CLIENT_SECRET redirectURI: ${REDIRECT_URI} scopes: - profile - email routes: - matches: - path: pathPrefix: / backends: - host: httpbin.httpbin.svc.cluster.local:8000 oidc: cookieSecretName: agentgateway-ui-secrets extraEnv: - name: UI_CLIENT_SECRET valueFrom: secretKeyRef: name: agentgateway-ui-secrets key: UI_CLIENT_SECRET EOFUpgrade the release with your values file.
helm upgrade -i agentgateway-standalone \ oci://cr.agentgateway.dev/charts/agentgateway-standalone \ --namespace agentgateway-system \ --version v1.5.0 \ --reuse-values \ -f values.yamlConfirm that the pod is running.
kubectl get pods -n agentgateway-system \ -l app.kubernetes.io/name=agentgateway-standalonePort-forward the UI port again, and confirm that an unauthenticated request is redirected to your IdP.
kubectl port-forward -n agentgateway-system \ deploy/agentgateway-standalone 4001:4001curl -s -o /dev/null -D- http://localhost:4001/ui | grep -i locationExample output:
location: https://keycloak.example.com/realms/agentgateway/protocol/openid-connect/auth?response_type=code&client_id=agentgateway-ui&...
Important
Agentgateway fetches the OIDC discovery document at startup, so the issuer must be reachable from the pod. When the fetch fails, the pod does not start, and the logs report failed to decode oidc discovery response from uri. If the pod enters CrashLoopBackOff after you add the policy, check the issuer URL and any egress restrictions.
Next steps
- Expose the UI on your own HTTPS hostname, now that a login is required.
- Choose where configuration is stored so that the UI can save your changes.