Docs

Set Up SSO with Auth0

Configure Apache APISIX with Auth0 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 Auth0, 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 Auth0 tenant and a test user that can authenticate through an enabled connection.
  • 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 Auth0 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.0 header.

  • The route ID auth0-sso and path /anything/user/* are unused. Check both with:

    curl -s -o /dev/null -w '%{http_code}\n' \
      "${APISIX_ADMIN_URL}/apisix/admin/routes/auth0-sso" \
      -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: 404 from the first command and no output from the second. A 200 or 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 register the Auth0 application, capture its OIDC settings, and create the protected APISIX route.

Step 1: Register a regular web application

In the Auth0 Dashboard, select Applications → Applications → Create Application, name it APISIX Authorization Code, choose Regular Web Applications, and create it.

On the application's Settings tab, add the exact value of ${SSO_CALLBACK_URL}—http://localhost:9080/anything/user/callback for this guide—to Allowed Callback URLs, then save the changes.

Auth0 compares callback URLs against the allowlist. In production, use a stable HTTPS origin and register the exact scheme, host, port, and path that APISIX sends as redirect_uri.

Expected: the application is a regular web application and its callback allowlist contains the APISIX callback URL exactly once.

What changed: Auth0 can return the browser only to the registered APISIX callback.

Step 2: Capture the OIDC settings

On the same Settings tab, copy the Domain, Client ID, and Client Secret into the shell:

read -rp "Auth0 domain, without https://: " AUTH0_DOMAIN && export AUTH0_DOMAIN
read -rp "Auth0 client ID: " AUTH0_CLIENT_ID && export AUTH0_CLIENT_ID
read -rsp "Auth0 client secret: " AUTH0_CLIENT_SECRET && export AUTH0_CLIENT_SECRET && echo
export AUTH0_DISCOVERY="https://${AUTH0_DOMAIN}/.well-known/openid-configuration"
APISIX_SESSION_SECRET="$(openssl rand -hex 32)" && export APISIX_SESSION_SECRET

Keep the client and session secrets out of shell history, source control, logs, and screenshots. Use a secret manager and rotation policy for production credentials.

Confirm that the discovery document advertises authorization and token endpoints:

curl -sS "${AUTH0_DISCOVERY}" | jq -er '.authorization_endpoint, .token_endpoint'

Expected: two HTTPS URLs under the configured Auth0 domain.

What changed: the shell holds the Auth0 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/auth0-sso" -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": "${AUTH0_CLIENT_ID}",
      "client_secret": "${AUTH0_CLIENT_SECRET}",
      "discovery": "${AUTH0_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": "auth0-sso", "uri": "/anything/user/*", "plugins": { "openid-connect": { "bearer_only": false, "use_pkce": true, … } }, … } }

What changed: /anything/user/* now requires an Auth0-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 Auth0. Sign in as the test user and accept the application prompt if Auth0 shows one.

Expected: Auth0 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 Auth0 sign-in prompt. A second new private window redirects to Auth0 again because it has no APISIX session cookie.

Roll back / Clean up

Delete the APISIX route before deleting its Auth0 client:

curl -s -X DELETE "${APISIX_ADMIN_URL}/apisix/admin/routes/auth0-sso" \
  -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.

In Auth0, delete APISIX Authorization Code only if no other environment uses it. Close the private browser window and unset the secrets:

unset AUTH0_CLIENT_SECRET APISIX_SESSION_SECRET APISIX_ADMIN_KEY

Troubleshoot

SymptomCauseFix
Auth0 reports a callback URL mismatch.The Auth0 allowlist and APISIX redirect_uri differ by scheme, host, port, or path.Copy ${SSO_CALLBACK_URL} exactly into Allowed Callback URLs and recreate the route if its value changed.
APISIX returns 500 or cannot start the redirect.The discovery document is unreachable, the client credentials are wrong, or the session secret is unsuitable.Fetch ${AUTH0_DISCOVERY} from the APISIX network, recopy the client values, and generate a new session secret.
Sign-in completes but APISIX reports no session state.The browser did not return the APISIX session cookie, or the authorization request expired.Use one consistent gateway origin, allow cookies for it, and restart the flow from the protected URL. See No Session State Found (opens in Plugin Hub docs).