Okta Integration
Shoehorn supports Okta as both an authentication provider (OIDC) and an orgdata provider (Okta Management API for user/team sync). This guide walks through the exact Okta app configuration, Helm values, and troubleshooting mapped to the error codes Shoehorn emits on /auth-error.
Prerequisites
Section titled “Prerequisites”- An Okta organization with admin access
- Shoehorn deployed via the Helm chart — see Helm deployment
- Your Shoehorn domain (e.g.
shoehorn.example.com) reachable from browsers that will sign in
1. Create the Okta App
Section titled “1. Create the Okta App”In the Okta admin console, go to Applications → Create App Integration and select:
- Sign-in method: OIDC – OpenID Connect
- Application type: Web Application
Then configure the following fields exactly — the non-default values matter:
| Field | Value |
|---|---|
| App name | Shoehorn |
| Client authentication | Client secret |
| Proof of possession (PKCE) | Require PKCE as additional verification |
| Client secrets | Generate one and copy it — you will store this as OKTA_CLIENT_SECRET |
| Grant type | ☑ Authorization Code ☑ Refresh Token |
| Refresh token behavior | Rotate token after every use |
| Sign-in redirect URIs | https://<your-domain>/api/v1/auth/callback |
| Sign-out redirect URIs | https://<your-domain> |
| Login initiated by | App Only |
| Controlled access | Assign to the Okta groups that should have Shoehorn access |
Save, then copy the Client ID — you will store this as OKTA_CLIENT_ID.
Optional: sign-on policy
Section titled “Optional: sign-on policy”If users cannot sign in after completing the rest of this guide, check Applications → Shoehorn → Sign On → Sign On Policy. A permissive starting rule is:
- Catch-all rule: Access = Allowed, Authentication = Any 2 factor types
Tighten this once signing in works end-to-end.
2. Add the Groups Claim
Section titled “2. Add the Groups Claim”Okta does not include group membership in the ID token by default. Without this claim, Shoehorn cannot map your Okta groups to Shoehorn roles — logins will land on /auth-error?code=GROUPS_CLAIM_MISSING.
- Security → API → Authorization Servers — select the
defaultauthorization server (or your custom one) - Claims tab → Add Claim
| Field | Value |
|---|---|
| Name | groups |
| Include in token type | ID Token, Always |
| Value type | Groups |
| Filter | Matches regex, value .* |
| Include in | Any scope |
Save. Use Okta’s Token Preview (same authorization server page) to confirm the token now carries a groups array.
3. Add the Tenant (okta_org) Claim
Section titled “3. Add the Tenant (okta_org) Claim”Required for production. Without it, every login fails with INVALID_CLAIMS and the user lands on /auth-error. Shoehorn uses this claim as the tenant id for every request; if it’s missing, the request is rejected.
- Go to Security → API → Authorization Servers and select the same authorization server you used for the groups claim
- Open the Claims tab and click Add Claim
| Field | Value |
|---|---|
| Name | okta_org |
| Include in token type | ID Token, Always |
| Value type | Expression |
| Value | Your Okta org id, or an expression that returns it. A literal like "acme" works for a single-tenant deploy. For per-user attribution use something like user.organization. |
| Include in | Any scope |
The value is an opaque tenant id, so pick something stable. Once a user has signed in with one value, switching to another would orphan their existing data.
Use Okta’s Token Preview to confirm the ID token now carries okta_org.
4. (Optional) Create an API Token for Orgdata Sync
Section titled “4. (Optional) Create an API Token for Orgdata Sync”If you want Shoehorn to sync users and teams proactively from Okta — rather than only resolving memberships on individual logins:
- Security → API → Tokens → Create Token
- Name:
Shoehorn Orgdata Sync - Copy the token — you will store this as
OKTA_API_TOKEN
The token runs as the creating user. If that user loses read access to users or groups, sync breaks silently — prefer creating the token as a dedicated service account.
5. Configure Shoehorn (Helm)
Section titled “5. Configure Shoehorn (Helm)”A working reference is in examples/values-okta.yaml. Minimum configuration:
global: domain: shoehorn.example.com # used for the callback-URI allowlist
auth: provider: okta okta: # From step 1 — no scheme, no trailing slash domain: your-org.okta.com clientId: 0oaXXXXXXXXXXXXXX
# Optional — enable orgdata sync from step 3 orgdata: enabled: true providers: ["okta"] primaryProvider: "okta"
# Bootstrap admin access before UI-side mappings exist adminAssignment: adminGroups: "shoehorn-admins"
secret: existingSecret: shoehorn-auth-secrets mappings: OKTA_CLIENT_SECRET: okta_client_secret OKTA_API_TOKEN: okta_api_token # only if orgdata is enabled SESSION_ENCRYPTION_KEY: session_encryption_key JWT_SECRET: jwt_secret AUTH_ENCRYPTION_KEY: auth_encryption_keyCreate the Kubernetes secret before installing:
kubectl create secret generic shoehorn-auth-secrets \ --from-literal=okta_client_secret='<client-secret>' \ --from-literal=okta_api_token='<api-token>' \ --from-literal=session_encryption_key="$(openssl rand -hex 32)" \ --from-literal=jwt_secret="$(openssl rand -hex 32)" \ --from-literal=auth_encryption_key="$(openssl rand -hex 32)"The chart’s schema enforces that auth.okta.domain and auth.okta.clientId are set whenever auth.provider: okta — helm install will refuse with a minLength: got 0, want 1 error if either is missing.
Helm values reference
Section titled “Helm values reference”| Path | Required | Description |
|---|---|---|
auth.provider | yes | Must be okta |
auth.okta.domain | yes | Okta org domain, e.g. your-org.okta.com. No https://, no trailing slash |
auth.okta.clientId | yes | Client ID from step 1 |
auth.okta.issuer | no | Issuer URL override; leave empty for the default |
auth.orgdata.enabled | no | Set true to sync users/teams from Okta |
auth.orgdata.providers | if orgdata | Include "okta" |
auth.orgdata.primaryProvider | if orgdata | "okta" when Okta is the sole provider |
auth.adminAssignment.adminGroups | recommended | Okta groups to bootstrap as tenant admins before UI-side mappings exist |
secret.mappings.OKTA_CLIENT_SECRET | yes | Key in the referenced secret holding the client secret |
secret.mappings.OKTA_API_TOKEN | if orgdata | Key holding the Management API token |
global.domain | yes | Used to build the redirect-URI allowlist |
6. How It All Connects
Section titled “6. How It All Connects”Once installed, a browser login flows end-to-end:
- User visits
https://shoehorn.example.com→ Shoehorn redirects to Okta - User authenticates in Okta → Okta redirects back to
/api/v1/auth/callbackwith an ID token - Shoehorn verifies the ID token signature against Okta’s JWKS (fetched from
auth.okta.domain), then validates issuer and audience - If the user doesn’t exist in Shoehorn, it JIT-provisions them using
sub,email, andgroupsclaims - Shoehorn resolves roles:
- First it checks
auth.adminAssignment.adminGroupsagainst the user’s Okta groups (bootstrap path) - Then it applies Shoehorn-side group → role mappings configured under Admin → Directory → Teams
- First it checks
- If
auth.orgdata.enabled, a targeted sync fires for the new user so team-based mappings resolve immediately rather than waiting for the next scheduled sync
Logout follows the OIDC RP-initiated logout spec:
- User clicks Sign out → browser hits
/api/v1/auth/logout - Shoehorn clears its session, then discovers Okta’s
end_session_endpointand redirects the browser there - Okta clears its own session and redirects back to your Shoehorn domain
- The user lands signed-out; re-opening Shoehorn prompts Okta login afresh
7. Troubleshooting
Section titled “7. Troubleshooting”When login fails, Shoehorn redirects to /auth-error?code=<CODE>. The code tells you where to look:
Symptom / code | Likely cause | Fix |
|---|---|---|
code=GROUPS_CLAIM_MISSING | Groups claim not configured on the authorization server | Repeat step 2. Use Okta’s Token Preview to confirm the claim appears. |
code=INVALID_CLAIMS | okta_org claim missing or empty | Repeat step 3. Use Token Preview to confirm okta_org is set. |
code=NO_ROLES | User has Okta groups, but none map to a Shoehorn role | Configure a mapping under Admin → Directory → Teams, or add the user’s Okta group to auth.adminAssignment.adminGroups. |
code=INVALID_DOMAIN | OKTA_DOMAIN is the full URL, not the bare domain, or the token endpoint is unreachable | Use your-org.okta.com, no scheme, no slash. Check your cluster’s egress network path to Okta. |
code=JWKS_UNAVAILABLE | Shoehorn couldn’t fetch Okta’s signing keys | Usually transient. If it persists, verify OKTA_DOMAIN and your cluster’s egress network path to Okta. |
| Login redirects back to Okta in a loop | Session cookie expiring at epoch 0 (very old Shoehorn build) | Upgrade to Shoehorn ≥ v0.4.3. |
| Test Connection fails with “invalid API token” | Token was revoked or the creating user was deactivated | Recreate the token as a dedicated service account. |
| Test Connection fails with “host unreachable” | OKTA_DOMAIN contains the full URL | Use your-org.okta.com, no scheme, no slash. |
| After logout, user is auto-signed back in | end_session_endpoint not called — misconfigured app | Confirm Sign-out redirect URIs in the Okta app includes https://<your-domain>. |
Decoding the ID token
Section titled “Decoding the ID token”When debugging claim issues, paste the ID token into jwt.ms and check:
issmatcheshttps://<domain>(orhttps://<domain>/oauth2/<authz-server-id>if you use a custom authorization server)audequals yourOKTA_CLIENT_IDgroupscontains the expected arraysubis the stable Okta user ID (e.g.00uXXXXXXXXXXXXXX)
Using the admin Test Connection button
Section titled “Using the admin Test Connection button”Once deployed, visit Admin → Integrations → External → Okta in Shoehorn. The Test Connection button calls the Okta Management API with your stored token and returns a green banner on success or the exact upstream error on failure. It’s the fastest way to verify OKTA_API_TOKEN before investigating deeper.
8. Provider Matrix
Section titled “8. Provider Matrix”Shoehorn lets you mix auth and orgdata providers:
| Authentication | Orgdata | Supported |
|---|---|---|
| Okta | Okta | ✅ Most common |
| Okta | Zitadel | ✅ |
| Zitadel | Okta | ✅ |
| Okta | none (orgdata disabled) | ✅ Relies on adminAssignment + in-app role assignment |
See Also
Section titled “See Also”- Identity Providers overview — all supported IdPs at a glance
- Group mappings — mapping IdP groups to Shoehorn teams and roles
- Team sync — multi-provider synchronization details
- Helm deployment — full Helm install reference