You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Adds OIDC single sign-on to the dashboard and makes username/password login toggleable, so the dashboard can be run SSO-only.
Went with OIDC rather than SAML: it covers JumpCloud fully (JumpCloud exposes a standard OIDC IdP), is generic across providers (Okta, Google, Entra ID, ...), and avoids the XML-signature machinery SAML needs. The implementation is a vanilla OpenID Connect authorization-code flow with PKCE, driven entirely by the issuer's .well-known/openid-configuration, so it isn't JumpCloud-specific.
How it works
New s3proxy/dashboard/oidc.py: discovery, PKCE, code exchange, ID-token claim verification (iss/aud/exp/nonce), optional email-domain allowlist. In-flight login state is kept in Redis (single-use, short TTL).
Routes under the existing {dashboard.path}/api: GET /api/authmodes, GET /api/oidc/login, GET /api/oidc/callback. The nginx reverse-proxy already forwards /api, so no proxy change.
The login page fetches /api/authmodes and renders an SSO button, the password form, or both.
At least one method must be enabled (startup error otherwise).
The ID token is read directly from the token endpoint (a back-channel HTTPS call to the issuer); per OIDC Core §3.1.3.7 signature validation MAY be skipped for tokens obtained this way over TLS, so standard claims are verified instead of fetching JWKS. No new runtime dependencies.
Configure (values.yaml)
dashboard:
enabled: trueauth:
password:
enabled: false # SSO-onlyoidc:
enabled: trueissuer: "https://oauth.id.jumpcloud.com/"clientId: "<client-id>"clientSecret: "<client-secret>"# or use existingSecretallowedDomains: "example.com"
JumpCloud: create an OIDC app, set the redirect URI to <dashboard-url>/dashboard/api/oidc/callback, issuer https://oauth.id.jumpcloud.com/. Client secret can live in your own Secret via dashboard.auth.oidc.existingSecret.
HTTPS status (before this change): the chart ran the proxy over plain HTTP in-cluster (server.noTls: true, no certs mounted); external HTTPS was only terminated at the Ingress. The session-cookie Secure flag tracks no_tls, and the OIDC redirect scheme is derived from X-Forwarded-Proto (pinnable via dashboard.auth.oidc.redirectUrl).
Added server.tls.existingSecret — point at an existing kubernetes.io/tls Secret to have the proxy pod terminate HTTPS itself:
When set: noTls forced off, secret mounted at server.certPath (tls.crt/tls.key → s3proxy.crt/s3proxy.key), probes switch to scheme: HTTPS, the session cookie becomes Secure, and the dashboard nginx talks to the proxy over HTTPS (proxy_ssl_verify off, since the cert is for the external hostname).
The OIDC client-secret existing-secret (dashboard.auth.oidc.existingSecret) was already part of the original commit.
New chart tests cover both pod-TLS-on and the unchanged default. helm lint, ruff, and the chart test suite pass.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds OIDC single sign-on to the dashboard and makes username/password login toggleable, so the dashboard can be run SSO-only.
Went with OIDC rather than SAML: it covers JumpCloud fully (JumpCloud exposes a standard OIDC IdP), is generic across providers (Okta, Google, Entra ID, ...), and avoids the XML-signature machinery SAML needs. The implementation is a vanilla OpenID Connect authorization-code flow with PKCE, driven entirely by the issuer's
.well-known/openid-configuration, so it isn't JumpCloud-specific.How it works
s3proxy/dashboard/oidc.py: discovery, PKCE, code exchange, ID-token claim verification (iss/aud/exp/nonce), optional email-domain allowlist. In-flight login state is kept in Redis (single-use, short TTL).{dashboard.path}/api:GET /api/authmodes,GET /api/oidc/login,GET /api/oidc/callback. The nginx reverse-proxy already forwards/api, so no proxy change./api/authmodesand renders an SSO button, the password form, or both.The ID token is read directly from the token endpoint (a back-channel HTTPS call to the issuer); per OIDC Core §3.1.3.7 signature validation MAY be skipped for tokens obtained this way over TLS, so standard claims are verified instead of fetching JWKS. No new runtime dependencies.
Configure (values.yaml)
JumpCloud: create an OIDC app, set the redirect URI to
<dashboard-url>/dashboard/api/oidc/callback, issuerhttps://oauth.id.jumpcloud.com/. Client secret can live in your own Secret viadashboard.auth.oidc.existingSecret.Tests
tests/unit/test_dashboard_oidc.py— PKCE, claim decode, full login flow (mocked IdP via httpx MockTransport), nonce/domain enforcement, single-use state,authmodes, password-disabled rejection, SSO-only.tests/unit/test_chart_dashboard_auth.py— chart rendering across secret/env wiring permutations.helm lint,svelte-check, and a frontend build all pass.