Okta OIDC
Overview
This article describes how to set up OpenID Connect (OIDC) authentication for KubeSense using Okta.
The browser performs the OAuth 2.0 authorization-code flow with PKCE against Okta, then posts the resulting ID token to KubeSense, which verifies its signature against Okta's published keys before establishing a session.
info: Okta supports both OIDC and SAML. Either works — SAML is the more common enterprise choice and additionally supports group-to-role mapping. See Okta SAML if you would rather use it.
Prerequisites
- An Okta organization with super administrator access
- A KubeSense deployment reachable at a public HTTPS URL (e.g.
https://kubesense.example.com) - The ability to run
helm upgradeagainst the deployment
Step 1: Create the OIDC app in Okta
- In the Okta Admin Console, go to Applications → Applications
- Click Create App Integration
- Select OIDC - OpenID Connect, then Single-Page Application, and click Next
- Configure:
| Field | Value |
|---|---|
| App integration name | KubeSense |
| Grant type | Authorization Code (PKCE is implied for SPAs) |
| Sign-in redirect URIs | https://<KUBESENSE_DOMAIN>/login/callback |
| Sign-out redirect URIs | https://<KUBESENSE_DOMAIN>/login |
| Controlled access | Choose who may use the app |
- Click Save, then copy the Client ID from the app's General tab
warning: The sign-in redirect URI must end in /login/callback, not /login. It must also include the scheme. A mismatch produces redirect_uri errors from Okta before the user ever reaches KubeSense.
Select Single-Page Application, not Web Application: KubeSense holds no client secret, and Okta rejects a confidential-client configuration used from a browser.
Step 2: Grant the app in the authorization server policy
This step is easy to miss and produces a confusing error if skipped.
- Go to Security → API → Authorization Servers
- Open the authorization server you intend to use — usually
default - Open the Access Policies tab
- Ensure a policy exists with a rule that applies to your app:
- Assigned to: All clients, or explicitly include the KubeSense app
- Grant type: Authorization Code
- Scopes:
openid,profile,email
- Save
warning: Without a matching policy rule, Okta returns 400 Bad Request — Policy evaluation failed for this request with error code access_denied at the authorization endpoint. This happens before any token is issued, so nothing appears in the KubeSense logs.
Note the Issuer URI shown on the authorization server page — you need it in Step 3. It is either:
- Custom authorization server:
https://<your-org>.okta.com/oauth2/default - Org authorization server:
https://<your-org>.okta.com
KubeSense resolves signing keys through OIDC discovery, so both layouts work.
Step 3: Configure KubeSense via Helm values
api:
# The API validates the ID token's `iss` and `aud` against these values and
# fetches the signing keys from this issuer's OIDC discovery document.
OKTA_ISSUER: "https://<your-org>.okta.com/oauth2/default"
OKTA_CLIENT_ID: "<client-id-from-step-1>"
webapp:
okta:
enabled: true
issuer: "https://<your-org>.okta.com/oauth2/default"
client_id: "<client-id-from-step-1>"warning: The issuer and client ID appear twice — once for the API and once for the webapp — and they must be identical. If they differ, the browser obtains a token from one Okta app and the API validates it against another, which fails with User email validation failed.
Apply with:
helm upgrade kubesense ./charts/kubesense-server -f values.yamlConfiguration reference
API environment variables
| Variable | Description |
|---|---|
OKTA_OAUTH2_ISSUER | Authorization server issuer. Rendered from api.OKTA_ISSUER. |
OKTA_CLIENT_ID | Application client ID. The ID token's aud is validated against this. |
OKTA_API_AUDIENCE | Legacy alias for OKTA_CLIENT_ID, still honoured. |
Webapp environment variables
| Variable | Description |
|---|---|
ENABLE_OKTA_AUTH | true renders the "Sign In with Okta" button |
OKTA_ISSUER | Authorization server issuer — must match the API |
OKTA_CLIENT_ID | Client ID — must match the API |
Step 4: Verify
- Open
https://<KUBESENSE_DOMAIN>/loginin a private browser window - Click Sign In with Okta
- Authenticate with your Okta account
- You should land on the KubeSense dashboard
New users are provisioned automatically on first login with the User role.
How tokens are validated
Understanding this makes the errors below easier to read. On every sign-in the API:
- Resolves the JWKS endpoint from
<issuer>/.well-known/openid-configuration - Verifies the ID token's RSA signature against the published key matching the token's
kid, refetching once if the key is unknown (so Okta key rotation is transparent) - Rejects any token not signed with RSA, which blocks algorithm-confusion attacks
- Validates
iss,audandexp - Rejects a token whose
email_verifiedclaim is explicitlyfalse - Reads the account's email from the
emailclaim, falling back topreferred_usernamewhen it contains an@
A token with no usable email is rejected rather than provisioning an account under an opaque identifier.
info: The OIDC nonce is validated in the browser by the Okta SDK before the token is submitted. The server cannot re-check it, since only the page that initiated the flow knows the expected value.
Scopes and claims
KubeSense requests openid, profile and email.
emailsupplies the account identity and is requiredprofilesuppliesname, used for the display name. If absent, the email's local part is used instead.
Single Logout
Signing out of KubeSense also calls Okta's signOut(), ending the Okta session. Note that this path does not currently invalidate the server-side KubeSense session record; the session expires on its own.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
400 Bad Request — Policy evaluation failed, access_denied | No authorization server policy rule covers the app | Complete Step 2 |
Okta redirect_uri error | Sign-in redirect URI mismatch | Must be exactly https://<domain>/login/callback, including scheme |
400 User email validation failed | ID token failed verification | Real reason is in the API logs under Error validating token from provider: |
Logs show signing key not found for key ID | The token came from a different Okta org or app than the API expects | Make OKTA_ISSUER identical for the webapp and the API. The logged error names the issuer actually used. |
Logs show 'iss' value doesn't match | Issuer mismatch | Check for a trailing slash, or /oauth2/default present on one side only |
Logs show 'aud' value doesn't match | Client ID mismatch | Make the webapp and API client IDs identical |
Logs show okta id token has no email claim | email scope not granted | Add email to the authorization server policy rule's scopes |
Logs show okta account email is not verified | Okta reports email_verified: false | Verify the user's email in Okta |
Logs show OKTA_OAUTH2_ISSUER is not set | Environment variable missing | Confirm api.OKTA_ISSUER is set and the pod was restarted |
400 Email has already been taken | The email already exists with a different sign-in method | The account was created via password or SAML. Use a different account, or change that user's auth_type/auth_provider. |
Spinner never leaves /login/callback | Login failed after the Okta redirect | Check for a toast message and the API logs; the underlying failure is one of the rows above |