SSO Setup (OIDC)
Wave 3.2.2 adds OpenID Connect (OIDC) single sign-on to the Web Console. This page is the operator-grade runbook — what to configure on your IdP, what to set in Helm, how to pre-provision users, and the IdP-specific gotchas to expect.
Why this approach
Kubernetes does not solve dashboard authentication. Console-style tools either ship without auth, depend on kubectl proxy plus a shared kubeconfig, or ship a single shared admin account — none of which passes a compliance review for an SRE team. Wave SSO federates to the IdP your organization already runs, so every engineer logs in as themselves and the audit trail is real. The original local admin survives as a break-glass path, so an IdP outage cannot lock you out.
Prerequisites
- Wave 3.2.2 or later.
- A standards-compliant OIDC identity provider (tested with Okta, Microsoft Entra ID (Azure AD), Google Workspace, Keycloak).
- The Wave Autoscale Helm chart with the SSO companion values applied. Helm wiring lives in the
wave-autoscale-helmcompanion repo — not in the core code repo. - Permission to register a new OIDC client in the IdP.
Generate the cookie key
OIDC state, nonce, and the PKCE verifier travel between the browser and Wave in a single encrypted cookie. The cookie is sealed with AES-256-GCM using a 32-byte key.
Generate one once per environment:
openssl rand -base64 32Use the output as WA_SSO_COOKIE_KEY in the next step. Treat it as a secret.
Register the OIDC client in your IdP
Generic OIDC setup (all IdPs):
| Setting | Value |
|---|---|
| Flow | Authorization Code with PKCE |
| Scopes | openid email profile |
| Redirect URI | https://<your-wave-public-host>/api/auth/sso/oidc/callback |
The redirect URI must match WA_SSO_OIDC_REDIRECT_URI exactly — protocol, host, port, path, trailing-slash. A common cause of "redirect URI mismatch" errors is an HTTPS terminator that adds or drops a trailing slash.
Configure Wave (WA_SSO_* environment variables)
Wave reads the following environment variables. The Helm chart maps them onto a wave-sso Secret + Deployment env (see the helm repo companion PR).
| Variable | Type | Default | Notes |
|---|---|---|---|
WA_SSO_OIDC_ENABLED | bool | false | Master switch. |
WA_SSO_OIDC_ISSUER | URL | — | Required when enabled. The IdP's issuer URL — the same URL whose /.well-known/openid-configuration document Wave will fetch. |
WA_SSO_OIDC_CLIENT_ID | string | — | Required. |
WA_SSO_OIDC_CLIENT_SECRET | secret | — | Required for confidential clients. |
WA_SSO_OIDC_REDIRECT_URI | URL | — | The public callback URL registered above. |
WA_SSO_OIDC_SCOPES | csv | openid,email,profile | Add groups only if your IdP gates group claims behind an explicit scope. |
WA_SSO_OIDC_EMAIL_CLAIM | string | email | Override only for Keycloak-only deployments (see IdP-specific notes). |
WA_SSO_OIDC_REQUIRE_VERIFIED_EMAIL | bool | true | Reject tokens where email_verified=false. Keep on unless you fully trust the IdP. |
WA_SSO_OIDC_CLOCK_SKEW_SECONDS | u64 | 60 | Token iat/exp validation tolerance. |
WA_SSO_OIDC_DISPLAY_NAME | string | — | Optional label shown on the login button (e.g. "Sign in with Okta"). |
WA_SSO_COOKIE_KEY | base64-32B | — | Required. Output of openssl rand -base64 32 from earlier. |
WA_SSO_DEV_INSECURE_COOKIES | bool | false | DEV ONLY — drops the Secure cookie attribute so http://localhost works. Never set in production. |
Pre-provision users
SSO users do not auto-create on first login. An admin must add the row first:
- Web Console → Admin → Users → Add user.
- Set Auth source = SSO.
- Set Email to the exact value the IdP will return in the claim named by
WA_SSO_OIDC_EMAIL_CLAIM(default:email). - Assign a role:
Viewer/Operator/Manager/Admin.
The first IdP login then binds to this row by email match. The Reset Password button is hidden for SSO users; POST /api/auth/change-password against an SSO row returns 400 Bad Request with error code E00051 ("This account is managed via SSO; password change is not supported.").
If a user's email later changes in the IdP, the Wave row must be updated manually — see "Why identity matches email" below for the reason.
The login experience
- On the login page, the default view shows "Sign in with
<WA_SSO_OIDC_DISPLAY_NAME>" (or "SSO" when unset). - A "Use local admin account" toggle expands the legacy username/password form. This toggle remains visible even when SSO is enabled — it is your break-glass path when the IdP is down.
- After a successful OIDC handshake, Wave issues its standard opaque
wau_…session token. The session model is identical to a local admin login from that point on.
Claims-mapping reference
| Wave field | OIDC claim (default) | Behavior on missing |
|---|---|---|
| Identity match | email (or whatever WA_SSO_OIDC_EMAIL_CLAIM points to) | Reject with sso_not_authorized. |
| Email verified | email_verified | Reject if false AND WA_SSO_OIDC_REQUIRE_VERIFIED_EMAIL=true. |
| Display name | name, then preferred_username | Falls back to the email local-part. |
IdP-specific notes
Okta
- Use the "OIDC - Web App" application template.
- Issuer URL:
https://<your-okta-domain>/oauth2/default(or a custom Authorization Server). - Group claims are gated behind a separate
groupsscope; only add it if you plan to surface groups later — Wave 3.2.2 does not use them.
Microsoft Entra ID (Azure AD)
- Redirect URI must be HTTPS even on single-tenant.
- The
emailclaim can be empty for personal Microsoft accounts. Restrict the app registration to your org tenant in the Supported account types picker to avoid this. - Issuer:
https://login.microsoftonline.com/<tenant-id>/v2.0.
Google Workspace
- The
hd(hosted domain) claim restricts logins to your Workspace domain. Enforce this in Google's app config rather than in Wave — Google will refuse the token before it reaches you. - Issuer:
https://accounts.google.com.
Keycloak
- Realm setting Login → Edit username = OFF (the default) enforces
preferred_usernameuniqueness and immutability. In Keycloak-only environments, you can switchWA_SSO_OIDC_EMAIL_CLAIM=preferred_usernameto make identity stable against email changes. - Do not do this if there is any chance you will swap IdPs later —
preferred_usernameportability is not guaranteed by the OIDC spec.
Why identity matches email and not sub
OpenID Connect Core 1.0 §5.7 is explicit: only the (iss, sub) pair is guaranteed stable and unique. email is not. We chose email-matching anyway, with WA_SSO_OIDC_REQUIRE_VERIFIED_EMAIL=true as the safety guard, because:
- Onboarding UX is simpler — admins pre-provision by email, which is what they already know.
- Email change in the IdP is a real but rare event, and the failure mode is "user can't log in until admin updates the row," not "user gets the wrong account."
- The verified-email requirement closes the uniqueness gap for the common IdPs.
Troubleshooting
sso_not_authorized— no Wave user row matches the email claim. Pre-provision the user and ensure the email matches the IdP claim exactly.E00051(400 Bad Request) — expected when an SSO user hitsPOST /api/auth/change-password. Message: "This account is managed via SSO; password change is not supported." The UI hides the Reset Password button for SSO users so this normally never surfaces.- "redirect URI mismatch" at the IdP — protocol, host, port, and path of
WA_SSO_OIDC_REDIRECT_URImust match the value registered in the IdP exactly. Re-check trailing slashes. - Cookie not set on
localhostduring development — setWA_SSO_DEV_INSECURE_COOKIES=true. Never set this in production; the cookie loses itsSecureflag. - OIDC handshake works but login fails with
email_not_verified— your IdP is returningemail_verified=false. Either fix the IdP-side verification, or lower the bar withWA_SSO_OIDC_REQUIRE_VERIFIED_EMAIL=falseif you accept the risk. - IdP outage and you cannot get in — use the "Use local admin account" toggle on the login screen. The original local admin remains valid.