Skip to content

UI NextAuth fails OIDC discovery over HTTP — local Helm install with bundled Keycloak unusable #2513

Description

@hunzai

Summary

In a local Helm install of oci://ghcr.io/i-am-bee/agentstack/chart/agentstack:0.7.1 with the bundled Keycloak and auth.enabled: true, the UI's NextAuth (Auth.js v5 / oauth4webapi) refuses to perform OIDC discovery against the in-cluster Keycloak URL because the URL is plain HTTP. NextAuth falls back to a degraded code path, the resulting access tokens fail audience validation on the backend, and every authenticated UI request returns 401. The user sees "Authentication Error / Server authentication failed" on the signin page and cannot log in. The CLI flow (agentstack server login) is unaffected.

The chart already has AUTH__OIDC__INSECURE_TRANSPORT=true on the agentstack-server deployment for this exact purpose. The agentstack-ui deployment has no equivalent flag, and there is no chart value to enable insecure transport for the UI.

Environment

  • Chart: oci://ghcr.io/i-am-bee/agentstack/chart/agentstack version 0.7.1
  • Kubernetes: k3s v1.34.6+k3s1 (single node, native Linux, no VM)
  • Helm: v3.18.4
  • Linux host (Ubuntu/Debian)
  • Access pattern: kubectl port-forward (no Ingress, no TLS), the documented local path
    • svc/agentstack-ui-svc 8334:8334
    • svc/agentstack-server-svc 8333:8333
    • svc/keycloak 8336:8336

Minimal config.yaml

externalRegistries:
  public_github: "https://github.com/i-am-bee/agentstack@v0.7.1#path=agent-registry.yaml"
providers:
  - location: ghcr.io/i-am-bee/agentstack/agents/chat:0.7.1
encryptionKey: "<fernet>"
auth:
  enabled: true
  basic:
    enabled: true
    adminPassword: "..."   # ignored by 0.7.x (separate issue)

helm upgrade --install agentstack -f config.yaml oci://ghcr.io/i-am-bee/agentstack/chart/agentstack --version 0.7.1 succeeds. All pods reach Running 1/1. The keycloak-provision job completes successfully.

Reproduction

  1. Create the user manually in the agentstack realm via kcadm.sh (or the Keycloak admin UI), with emailVerified=true and the agentstack-admin realm role assigned. (The chart provides no out-of-the-box first-user seeding for a local install.)
  2. Open http://localhost:8334 in a browser (private window to rule out cookies).
  3. Sign in via the Keycloak page with the user above.
  4. Browser is redirected back to http://localhost:8334/signin?callbackUrl=%2F showing "Server authentication failed".

What the logs show

agentstack-ui pod

OIDC discovery failed for http://keycloak:8336/realms/agentstack, using fallback: l: only requests to HTTPS are allowed
    at m (.next/server/chunks/4647.js:404:135201)
    ...
    at async p (.next/server/app/api/auth/[...nextauth]/route.js:1:8150)
    at async q (.next/server/app/api/auth/[...nextauth]/route.js:1:8916)
    at async Object.jwt (.next/server/app/api/auth/[...nextauth]/route.js:1:11502)
{
  code: 'OAUTH_HTTP_REQUEST_FORBIDDEN',
  [cause]: v: only requests to HTTPS are allowed
      ...
      [cause]: URL {
        href: 'http://keycloak:8336/realms/agentstack',
        protocol: 'http:',
        hostname: 'keycloak',
        port: '8336',
        pathname: '/realms/agentstack',
      }
}

agentstack-server pod

warning  Token validation failed: invalid_claim: Invalid claim 'aud'   [agentstack_server.api.dependencies]
error    Error during HTTP request: [('HTTPException', '401: Token validation failed')]
info     10.42.0.24:51730 - "GET /api/v1/providers HTTP/1.1" 401

The aud failure is a downstream symptom of the discovery fallback, not the root cause — see analysis below.

Root cause

NextAuth v5 / Auth.js uses oauth4webapi for OIDC. oauth4webapi rejects any HTTP URL for processDiscoveryResponse / token endpoint requests unless the hostname is exactly localhost, 127.0.0.1, or [::1]. Hostnames like keycloak (the in-cluster K8s service DNS name) are blocked, even though the request is purely intra-cluster.

The chart's agentstack-ui deployment template (helm/templates/ui/deployment.yaml at v0.7.1) sets:

- name: OIDC_PROVIDER_ISSUER
  value: {{ include "agentstack.oidc.internalIssuerUrl" . | quote }}     # http://keycloak:8336/realms/agentstack
- name: OIDC_PROVIDER_EXTERNAL_ISSUER
  value: {{ include "agentstack.oidc.publicIssuerUrl" . | quote }}       # http://localhost:8336/realms/agentstack

There is no OIDC_INSECURE_TRANSPORT / AUTH_TRUST_HOST_HTTP / equivalent on the UI container. By contrast, the server template sets:

- name: AUTH__OIDC__INSECURE_TRANSPORT
  value: {{ hasPrefix "http://" (include "agentstack.oidc.internalIssuerUrl" . | default "") | quote }}

so the backend correctly opts in to HTTP. The UI cannot.

The fallback path NextAuth takes when discovery fails issues tokens that don't carry the agentstack-server-audience scope, which is why the backend then rejects them with Invalid claim 'aud'. Fixing the audience without fixing discovery does not help (verified by manually attaching agentstack-server-audience as a default scope to the agentstack-ui Keycloak client — server still 401s because the token NextAuth ends up forwarding is from the fallback path).

Verification that the rest of the stack is correct

Issuing a token directly via the Keycloak ROPC grant on the agentstack-ui client (with the audience scope manually attached) and calling the API works end-to-end:

TOKEN=$(curl -s -X POST http://localhost:8336/realms/agentstack/protocol/openid-connect/token \
  -d grant_type=password -d client_id=agentstack-ui -d client_secret=$UI_SECRET \
  -d username=hunzai -d password=*** -d scope=openid \
  | jq -r .access_token)

curl -i -H "Authorization: Bearer $TOKEN" http://localhost:8333/api/v1/providers
# HTTP/1.1 200 OK
# {"items":[{"source":"ghcr.io/i-am-bee/agentstack/agents/rag:0.7.1",...}]}

So Keycloak, the realm, the user, the audience mappers, and the server's token validation all work. The failure is purely in the UI's NextAuth → Keycloak discovery step.

Impact

A first-time user following the deployment guide for a local Helm install on Linux with port-forwarding (no Ingress, no TLS) cannot access the Web UI at all with auth enabled. Combined with the related issue that basic auth is stale and ignored, the only working configuration for this very common scenario is auth.enabled: false.

Proposed fix

Add an opt-in chart value, e.g.:

auth:
  uiInsecureTransport: false  # default

When set to true, the chart should:

  1. Set an env var on the agentstack-ui container that tells Auth.js / oauth4webapi to allow HTTP. There are a few possible mechanisms:
    • Pass OIDC_PROVIDER_ISSUER=http://localhost:8336/... on the UI container together with a hostAliases entry mapping localhost is not possible — but wiring NextAuth's underlying client through [customFetch] with [allowInsecureRequests] is. The UI app would need a small code change to plumb the flag through.
    • Or set process.env.AUTH_TRUST_HOST=true and call oauth4webapi with the [allowInsecureRequests] symbol on the issuer config when an env flag is present.
  2. Auto-enable when keycloak.publicIssuerUrl and auth.nextauthUrl both start with http:// (mirrors how AUTH__OIDC__INSECURE_TRANSPORT is auto-derived for the server).
  3. Default to false (HTTPS-only) so production installs are unaffected.

Alternatively, document clearly in the deployment guide that auth must be disabled for any HTTP-only local install with chart 0.7.x — currently the docs imply OIDC works locally out of the box, which it does not.

Workaround for affected users

Set auth.enabled: false in values.yaml and helm upgrade. This is the only working configuration for a local HTTP install. The CLI auth path (agentstack server login) is unaffected and continues to work even with OIDC enabled (because the CLI uses a public client and a real browser, not Auth.js server-side).

Related

  • Existing issue noting that the deployment guide's basic auth section is stale and that auth is now binary (disabled vs OIDC). The current report is the OIDC half of that picture: OIDC also doesn't work in the most common local setup.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingui

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions