Set Up SSO with Amazon Cognito
Configure Apache APISIX with Amazon Cognito 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:25 minutesImpact:No restart, no traffic change
Goal
A route that redirects unauthenticated browser requests to Amazon Cognito managed login, 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 an AWS account with permission to manage Amazon Cognito user pools, app clients, domains, and users.
- 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 Cognito and APISIX.
Creating a user pool and signing in a test user can affect Amazon Cognito usage and billing. Review Amazon Cognito pricing, prefer an isolated test pool, and delete guide-specific resources when finished.
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
cognito-ssoand path/anything/user/*are unused. Check both with:curl -s -o /dev/null -w '%{http_code}\n' \ "${APISIX_ADMIN_URL}/apisix/admin/routes/cognito-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:
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
Four steps create an OIDC user-pool application, create a test user, capture its settings, and create the protected APISIX route.
Step 1: Create the user pool and web application
In the AWS Management Console, open Amazon Cognito → User pools and create an isolated user directory:
- Select Create user pool.
- Select Traditional web application as the application type.
- Name the application
APISIX Authorization Code. - Select Email as the sign-in identifier.
- Disable self-registration unless the test requires it.
- Enter the exact value of
${SSO_CALLBACK_URL}—http://localhost:9080/anything/user/callbackfor this guide—as the return URL. - Create the user directory.
Cognito creates the user pool, a confidential app client, a domain, and managed login pages. Under Applications → App clients, open APISIX Authorization Code, select Login pages → Edit, and verify:
- Authorization code grant is the only OAuth grant.
- The callback allowlist contains
${SSO_CALLBACK_URL}exactly once. - The allowed OIDC scopes include
openidandemail.
Expected: the app client has a secret, one exact callback URL, Authorization Code enabled, and no Implicit or Client Credentials grant.
What changed: Cognito has an isolated OIDC authorization server and confidential web client that can return the browser to APISIX.
Step 2: Create a test user
In the user pool, select User management → Users → Create user. Do not send an invitation, enter a test email address, mark it verified, and set a temporary password that meets the pool policy.
Expected: the test user appears in the user list. The user must replace the temporary password at first sign-in.
What changed: the isolated pool has one account that can complete managed-login validation.
Step 3: Capture the OIDC settings
Copy the User pool ID from the pool overview. Open APISIX Authorization Code under Applications → App clients and copy its Client ID and Client secret into the shell:
read -rp "Cognito AWS Region: " COGNITO_REGION && export COGNITO_REGION
read -rp "Cognito user pool ID: " COGNITO_USER_POOL_ID && export COGNITO_USER_POOL_ID
read -rp "Cognito SSO client ID: " COGNITO_SSO_CLIENT_ID && export COGNITO_SSO_CLIENT_ID
read -rsp "Cognito SSO client secret: " COGNITO_SSO_CLIENT_SECRET && export COGNITO_SSO_CLIENT_SECRET && echo
export COGNITO_ISSUER="https://cognito-idp.${COGNITO_REGION}.amazonaws.com/${COGNITO_USER_POOL_ID}"
export COGNITO_DISCOVERY="${COGNITO_ISSUER}/.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. Use a secret manager and rotation policy for production credentials.
Confirm that the discovery document advertises authorization and token endpoints:
curl -sS "${COGNITO_DISCOVERY}" | jq -er '.authorization_endpoint, .token_endpoint'Expected: two HTTPS URLs associated with the user pool.
What changed: the shell holds the Cognito client configuration and a fresh 256-bit secret for encrypting and authenticating the APISIX session cookie.
Step 4: 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/cognito-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": "${COGNITO_SSO_CLIENT_ID}",
"client_secret": "${COGNITO_SSO_CLIENT_SECRET}",
"discovery": "${COGNITO_DISCOVERY}",
"redirect_uri": "${SSO_CALLBACK_URL}",
"bearer_only": false,
"use_pkce": true,
"scope": "openid 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": "cognito-sso", "uri": "/anything/user/*", "plugins": { "openid-connect": { "bearer_only": false, "use_pkce": true, … } }, … } }What changed: /anything/user/* now requires a Cognito-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 Cognito managed login. Sign in as the test user and replace the temporary password if prompted.
Expected: Cognito 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 Cognito sign-in prompt. If the managed-login request takes longer than five minutes, start again from the protected URL.
Roll back / Clean up
Delete the APISIX route before deleting the Cognito resources it depends on:
curl -s -X DELETE "${APISIX_ADMIN_URL}/apisix/admin/routes/cognito-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.
If this guide created an isolated pool and it contains nothing else, delete the pool. If you reused a pool, delete only the APISIX Authorization Code app client and test user after confirming that no other integration uses them. Close the private browser window and unset the secrets:
unset COGNITO_SSO_CLIENT_SECRET APISIX_SESSION_SECRET APISIX_ADMIN_KEYTroubleshoot
| Symptom | Cause | Fix |
|---|---|---|
| Cognito reports a callback URL mismatch. | The app-client allowlist and APISIX redirect_uri differ by scheme, host, port, or path. | Copy ${SSO_CALLBACK_URL} exactly into the app client's 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 ${COGNITO_DISCOVERY} from the APISIX network, recopy the client values, and generate a new session secret. |
| Cognito shows an error after a long sign-in attempt. | Managed-login authentication did not complete within its time limit. | Start a new flow from ${GATEWAY_URL}/anything/user/get and complete sign-in promptly. |
| 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
- Authorize M2M Requests with Amazon Cognito — protect service-to-service requests with Cognito access tokens.
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.- Amazon Cognito app-client settings — AWS's current grant, callback, secret, and managed-login guidance.