Skip to content

OIDC login loop: frontend repeatedly initiates /auth/login and overwrites radar_oidc_state #1100

Description

@bitbloxhub

Drafted by ChatGPT

Describe the bug

When using OIDC auth, Radar can get stuck in a login loop during initial frontend boot.

A single browser tab repeatedly initiates /auth/login from the JavaScript bundle after protected /api/* requests return 401. Each /auth/login generates a new radar_oidc_state cookie. Because that cookie changes on each redirect/reload, callbacks from earlier OIDC redirects can fail or be canceled.

Eventually one callback may “win” and the app starts working, but the initial login flow is flaky and noisy.

To reproduce

Steps to reproduce the behavior:

  1. Run Radar 1.7.6 with OIDC enabled.
  2. Use a normal browser session with no valid Radar session.
  3. Open Radar, for example /traffic.
  4. Open browser DevTools with Network logs preserved.
  5. Observe protected API calls such as /api/connection, /api/capabilities, /api/namespaces, /api/portforwards, etc. returning 401.
  6. Observe /auth/login being initiated by the JS bundle.
  7. Observe the radar_oidc_state cookie changing repeatedly across reloads/redirects.
  8. Observe some callbacks failing with 400 or 500, or token exchange cancellation.
  9. Eventually, after one callback succeeds and the app settles, the same API calls return 200.

Expected behavior

Radar should start at most one OIDC login flow during frontend boot.

Repeated protected API 401s from the same SPA load should not independently initiate multiple /auth/login navigations or repeatedly overwrite radar_oidc_state.

After a successful OIDC callback, the browser should land back in the app with a valid session, and API calls should return 200 without repeated login redirects.

Screenshots

Not included.

Diagnostics (optional)

  • Radar version: ghcr.io/skyhook-io/radar:1.7.6
  • Kubernetes: v1.35.4+k3s1
  • Auth mode: OIDC
  • OIDC provider: Authentik
  • Browser: Firefox
  • Radar replicas: 1
  • RADAR_AUTH_SECRET: configured and stable
  • Browser tabs: only one Radar tab open

Logs

2026/07/04 18:12:01 [oidc] Using IdP-provided session ID (sid claim present)
2026/07/04 18:12:01 [oidc] User bitbloxhub@local.invalid authenticated (groups: [])
2026/07/04 18:12:02 "GET http://radar.k3s-podman-testing.localhost:4962/api/connection HTTP/1.1" ... - 401 54B
2026/07/04 18:12:02 "GET http://radar.k3s-podman-testing.localhost:4962/auth/login HTTP/1.1" ... - 302 314B

2026/07/04 18:12:03 [oidc] Using IdP-provided session ID (sid claim present)
2026/07/04 18:12:03 "GET http://radar.k3s-podman-testing.localhost:4962/auth/callback?code=...&state=..." ... - 302 24B
2026/07/04 18:12:03 [oidc] User bitbloxhub@local.invalid authenticated (groups: [])
2026/07/04 18:12:03 "GET http://radar.k3s-podman-testing.localhost:4962/api/connection HTTP/1.1" ... - 401 54B

2026/07/04 18:13:23 "GET http://radar.k3s-podman-testing.localhost:4962/auth/callback?code=...&state=..." ... - 400 44B
2026/07/04 18:13:29 [oidc] Token exchange failed: Post "http://authentik.k3s-podman-testing.localhost:4962/application/o/token/": context canceled
2026/07/04 18:13:29 "GET http://radar.k3s-podman-testing.localhost:4962/auth/callback?code=...&state=..." ... - 500 22B

# Eventually one callback succeeds and the app starts working:

2026/07/04 18:14:13 [oidc] Using IdP-provided session ID (sid claim present)
2026/07/04 18:14:13 "GET http://radar.k3s-podman-testing.localhost:4962/auth/callback?code=...&state=..." ... - 302 24B
2026/07/04 18:14:13 [oidc] User bitbloxhub@local.invalid authenticated (groups: [])
2026/07/04 18:14:15 "GET http://radar.k3s-podman-testing.localhost:4962/api/connection HTTP/1.1" ... - 200 234B
2026/07/04 18:14:15 "GET http://radar.k3s-podman-testing.localhost:4962/api/auth/me HTTP/1.1" ... - 200 91B
2026/07/04 18:14:20 "GET http://radar.k3s-podman-testing.localhost:4962/api/traffic/flows?since=5m HTTP/1.1" ... - 200 474439B
2026/07/04 18:14:20 [hubble] Retrieved 1000 flows

Deployment details:

replicas: 1

args:
  - --auth-cookie-ttl=4h
  - --auth-user-header=X-Forwarded-User
  - --auth-groups-header=X-Forwarded-Groups
  - --auth-oidc-issuer=http://authentik.k3s-podman-testing.localhost:4962/application/o/radar/
  - --auth-oidc-client-id=radar
  - --auth-oidc-redirect-url=http://radar.k3s-podman-testing.localhost:4962/auth/callback
  - --auth-oidc-groups-claim=entitlements
  - --auth-oidc-scopes=openid,profile,email,entitlements

env:
  - name: RADAR_AUTH_SECRET
    value: radar-auth-secret

image: ghcr.io/skyhook-io/radar:1.7.6

I also checked for stale/chunked/bad session-cookie symptoms and did not see any relevant log lines:

kubectl -n radar logs deploy/radar --since=60m \
  | rg -i 'HMAC|Session cookie|cookie|expired|chunk|too large|dropping ID token'

No matches.

The only Radar cookie present after a working login was radar_session.

Additional context

Using workaround for #981:

hostAliases:
  - ip: "127.0.0.1"
    hostnames:
      - "authentik.k3s-podman-testing.localhost"

containers:
  - name: authentik-issuer-loopback
    image: ghcr.io/nicolaka/netshoot:v0.15
    command:
      - socat
    args:
      - TCP-LISTEN:4962,fork,reuseaddr
      - TCP:authentik-server.authentik.svc.cluster.local:80

In-pod reachability looked stable:

50/50 requests to /.well-known/openid-configuration returned 200
connect time: ~0.0002–0.0009s
total time: ~0.53–0.86s

POST /application/o/token/ without form data returned:
code=400 total=0.084344

This looks like a frontend auth-flow issue rather than an IdP reachability issue.

In Firefox DevTools, the initiator for /auth/login is the JS bundle. During the failing phase, radar_oidc_state changes repeatedly on reload/redirect. This suggests multiple protected API 401s during SPA boot independently trigger /auth/login, which repeatedly overwrites the single OIDC state cookie before earlier callbacks complete.

Possible fixes:

  • Frontend: single-flight OIDC login redirects globally.
  • Frontend: do not redirect independently from every protected API 401 during initial boot.
  • Frontend: gate protected bootstrap API calls behind one auth/session check.
  • Backend hardening: make /auth/login idempotent while a valid radar_oidc_state already exists.
  • Backend hardening: allow multiple outstanding OIDC states instead of one shared state cookie.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions