Set Up SSO with Okta
Configure Apache APISIX with Okta for browser-based single sign-on using the OpenID Connect Authorization Code flow with PKCE.
Applies to:Apache APISIX 3.18.0
Roles:Platform engineerSecurity engineerTime:20 minutesImpact:No restart, no traffic change
Goal
A route that redirects unauthenticated browser requests to Okta, completes the OpenID Connect (OIDC) Authorization Code flow with Proof Key for Code Exchange (PKCE), keeps tokens in an encrypted APISIX session, and removes authentication data before proxying upstream.
Before you begin
- You have administrator access to an Okta organization and a test user or group that can be assigned to an application.
- You have an Apache APISIX installation, cURL, jq, and OpenSSL. Get APISIX installs APISIX in Docker or Kubernetes.
- Your browser can reach
http://localhost:9080. For any non-local deployment, substitute one stable HTTPS gateway origin in both Okta and APISIX.
Set the APISIX variables this guide uses:
export APISIX_ADMIN_URL="http://127.0.0.1:9180"
export GATEWAY_URL="http://localhost:9080"
export SSO_CALLBACK_URL="${GATEWAY_URL}/anything/user/callback"
read -rsp "Admin API key: " APISIX_ADMIN_KEY && export APISIX_ADMIN_KEY && echo-
The Admin API accepts your key. Check it with:
curl -s -o /dev/null -w '%{http_code}\n' "${APISIX_ADMIN_URL}/apisix/admin/routes" \ -H "X-API-KEY: ${APISIX_ADMIN_KEY}"Expected:
200. -
The data plane is reachable. Check it with:
curl -sI "${GATEWAY_URL}" | grep -i '^server'Expected: a
Server: APISIX/3.18.0header. -
The route ID
okta-browserand path/anything/user/*are unused. Check both with:curl -s -o /dev/null -w '%{http_code}\n' \ "${APISIX_ADMIN_URL}/apisix/admin/routes/okta-browser" \ -H "X-API-KEY: ${APISIX_ADMIN_KEY}" curl -s "${APISIX_ADMIN_URL}/apisix/admin/routes" \ -H "X-API-KEY: ${APISIX_ADMIN_KEY}" | \ jq -r '.list[] | select(.value.uri == "/anything/user/*") | .key'Expected:
404from the first command and no output from the second. A200or listed key means a collision; stop and choose a unique route ID and path rather than overwriting or shadowing an existing route.
Steps
Three steps create and assign the Okta application, capture its OIDC settings, and create the protected APISIX route.
Step 1: Create and assign the app integration
In the Okta Admin Console, select Applications and Resources → Applications → Create App Integration, choose OIDC - OpenID Connect, choose Web Application, and continue. Configure:
- App integration name:
APISIX Authorization Code - Grant type: Authorization Code only
- Sign-in redirect URI: the exact value of
${SSO_CALLBACK_URL}—http://localhost:9080/anything/user/callbackfor this guide - Controlled access: a bounded test group, or skip assignment during creation and assign a specific test user afterward
Save the application. On Assignments, verify that only the intended test user or group can access it.
Expected: a Web OIDC application enables Authorization Code, has one exact APISIX sign-in callback, and is assigned to the intended validation identity.
What changed: Okta can authenticate the assigned identity and return an authorization code only to the registered APISIX callback.
Step 2: Capture the OIDC settings
On the application's General tab, copy the Client ID and Client Secret. Use the Okta organization domain for endpoints, not an Admin Console host that contains -admin:
read -rp "Okta organization domain, without https://: " OKTA_DOMAIN && export OKTA_DOMAIN
read -rp "Okta client ID: " OKTA_CLIENT_ID && export OKTA_CLIENT_ID
read -rsp "Okta client secret: " OKTA_CLIENT_SECRET && export OKTA_CLIENT_SECRET && echo
export OKTA_DISCOVERY="https://${OKTA_DOMAIN}/.well-known/openid-configuration"
APISIX_SESSION_SECRET="$(openssl rand -hex 32)" && export APISIX_SESSION_SECRETKeep the client and session secrets out of shell history, source control, logs, and screenshots. Confirm that the discovery document advertises authorization and token endpoints:
curl -sS "${OKTA_DISCOVERY}" | jq -er '.authorization_endpoint, .token_endpoint'Expected: two HTTPS URLs under the Okta organization domain.
What changed: the shell holds the confidential web-client configuration and a fresh 256-bit secret for encrypting and authenticating the APISIX session cookie.
Step 3: Create the protected route
Create a route that starts browser authentication, uses PKCE, and strips credentials before proxying to httpbin.org:
curl -s "${APISIX_ADMIN_URL}/apisix/admin/routes/okta-browser" -X PUT \
-H "X-API-KEY: ${APISIX_ADMIN_KEY}" \
-H "Content-Type: application/json" \
--data-binary @- <<EOF
{
"uri": "/anything/user/*",
"plugins": {
"openid-connect": {
"client_id": "${OKTA_CLIENT_ID}",
"client_secret": "${OKTA_CLIENT_SECRET}",
"discovery": "${OKTA_DISCOVERY}",
"redirect_uri": "${SSO_CALLBACK_URL}",
"bearer_only": false,
"use_pkce": true,
"scope": "openid profile email",
"session": {"secret": "${APISIX_SESSION_SECRET}"},
"set_access_token_header": false,
"set_id_token_header": false,
"set_userinfo_header": false
},
"proxy-rewrite": {
"headers": {"remove": ["Authorization", "Cookie"]}
}
},
"upstream": {
"type": "roundrobin",
"nodes": {"httpbin.org:80": 1}
}
}
EOF{ "value": { "id": "okta-browser", "uri": "/anything/user/*", "plugins": { "openid-connect": { "bearer_only": false, "use_pkce": true, … } }, … } }What changed: /anything/user/* now requires an Okta-backed browser session. APISIX removes the original authorization and cookie headers and does not add token or user-information headers upstream. If the upstream application needs its own cookies, narrow the Cookie removal policy before production use.
Validate
Open ${GATEWAY_URL}/anything/user/get in a private browser window. APISIX redirects the browser to Okta. Sign in as the assigned test user.
Expected: Okta returns the browser to ${SSO_CALLBACK_URL}, APISIX resumes the original request, and httpbin returns a JSON response for /anything/user/get.
In the response's headers object, confirm that Authorization, Cookie, X-Access-Token, X-Id-Token, and X-Userinfo are absent. Reload the protected URL.
Expected: the reload succeeds through the APISIX session without another Okta sign-in prompt. An unassigned user is denied by Okta and never reaches the upstream.
Roll back / Clean up
Delete the APISIX route before deleting its Okta application:
curl -s -X DELETE "${APISIX_ADMIN_URL}/apisix/admin/routes/okta-browser" \
-H "X-API-KEY: ${APISIX_ADMIN_KEY}"Expected: the Admin API returns a successful deletion response, and the protected path no longer matches this route.
Unassign the test identity, then delete APISIX Authorization Code only if no other environment uses it. Close the private browser window and unset the secrets:
unset OKTA_CLIENT_SECRET APISIX_SESSION_SECRET APISIX_ADMIN_KEYTroubleshoot
| Symptom | Cause | Fix |
|---|---|---|
| Okta reports that the redirect URI is invalid. | The Sign-in redirect URI and APISIX redirect_uri differ. | Copy ${SSO_CALLBACK_URL} exactly into Sign-in redirect URIs and recreate the route if its value changed. |
| Okta denies a valid organization user. | The user or one of their groups is not assigned to the application. | Review Assignments and grant only the intended test user or group access. |
| APISIX cannot start or complete the authorization flow. | The -admin host was used for discovery, the client secret is wrong, or APISIX cannot reach Okta. | Use the organization domain, fetch ${OKTA_DISCOVERY} from the APISIX network, and recopy the client secret. |
| Sign-in completes but APISIX reports no session state. | The browser did not return the APISIX session cookie, or the gateway origin changed during the flow. | Use one consistent gateway origin, allow cookies for it, and restart the flow. See No Session State Found (opens in Plugin Hub docs). |
Related
openid-connectplugin overview (opens in Plugin Hub docs) — understand the authorization-code flow and common failures.openid-connectplugin reference (opens in Plugin Hub docs) — session, redirect, PKCE, and upstream-header options.- Okta web-app redirect sign-in — Okta's current Web OIDC application, callback, assignment, and credential guidance.