OIDC Flow ignores presented bearer tokens (kubelogin/kubectl gets a 302 to login) #44

Closed
opened 2026-09-16 05:30:12 +00:00 by ginjiruu · 0 comments
Owner

Symptom

kubectl get nodes through the k8s-hub gateway (auth via kubelogin) fails with:

serializer for text/html; charset=utf-8 doesn't exist

kubectl is receiving an HTML page — a 302 redirect to the Kanidm login — instead of a JSON API response.

Root cause (as understood)

internal/serve/oidc.go Check() only reads the session cookie (sessionTokens()cookie header). It never inspects the Authorization header. A token-presenting client (kubectl/kubelogin, which has no session cookie) falls straight through to relogin() → 302.

The OIDC Flow was designed for browser sessions only (cookie + lazy refresh). It has no path for a client that presents a bearer token directly.

Proposed direction (to be grilled)

Add a token-presentation path to the OIDC Flow, before the cookie check:

  1. Read the Authorization: Bearer token.
  2. Validate it against the Snapshot's JWKS (issuer + audience = clientID, ES256).
  3. On success: return OK and forward that token as the upstream Authorization header, letting the apiserver's own --oidc-* config do the final authorization.
  4. On validation failure: fall through to the existing cookie / relogin logic.

Open questions

  • Is the token kubelogin hands kubectl an access_token or an id_token? The existing verifier (newIDTokenVerifier) is id_token-oriented; both are ES256 from the same Kanidm JWKS and carry iss/aud, but we should confirm which type Kanidm actually returns to kubelogin before picking the validation path.
  • Should the bridge forward the presented token as-is, or re-issue/inject anything? (Forwarding as-is keeps the apiserver as the authz authority.)
  • Does this change the fail-closed status model (ADR 0011) for the token path?
## Symptom `kubectl get nodes` through the k8s-hub gateway (auth via kubelogin) fails with: ``` serializer for text/html; charset=utf-8 doesn't exist ``` kubectl is receiving an HTML page — a **302 redirect to the Kanidm login** — instead of a JSON API response. ## Root cause (as understood) `internal/serve/oidc.go` `Check()` only reads the **session cookie** (`sessionTokens()` → `cookie` header). It never inspects the `Authorization` header. A token-presenting client (kubectl/kubelogin, which has no session cookie) falls straight through to `relogin()` → 302. The OIDC Flow was designed for **browser sessions only** (cookie + lazy refresh). It has no path for a client that presents a bearer token directly. ## Proposed direction (to be grilled) Add a token-presentation path to the OIDC Flow, before the cookie check: 1. Read the `Authorization: Bearer` token. 2. Validate it against the Snapshot's JWKS (issuer + audience = clientID, ES256). 3. On success: return OK and **forward that token** as the upstream `Authorization` header, letting the apiserver's own `--oidc-*` config do the final authorization. 4. On validation failure: fall through to the existing cookie / relogin logic. ## Open questions - Is the token kubelogin hands kubectl an **access_token** or an **id_token**? The existing verifier (`newIDTokenVerifier`) is id_token-oriented; both are ES256 from the same Kanidm JWKS and carry `iss`/`aud`, but we should confirm which type Kanidm actually returns to kubelogin before picking the validation path. - Should the bridge forward the presented token as-is, or re-issue/inject anything? (Forwarding as-is keeps the apiserver as the authz authority.) - Does this change the fail-closed status model (ADR 0011) for the token path?
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lab/authz-bridge#44
No description provided.