Set Up SSO with Google
Configure Apache APISIX with Google 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 Google, 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 a Google Cloud project and permission to configure Google Auth Platform and create OAuth clients.
- You have a Google Account in the intended audience, or one that you can add as a test user when the app is External and in Testing status.
- 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 Google 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
google-ssoand path/anything/user/*are unused. Check both with:curl -s -o /dev/null -w '%{http_code}\n' \ "${APISIX_ADMIN_URL}/apisix/admin/routes/google-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
Three steps configure the Google Auth Platform audience, create the OAuth client, and create the protected APISIX route.
Step 1: Configure the application and audience
In the Google Cloud console, select the project and open Google Auth Platform → Overview. If the app is not configured, select Get started and provide:
- App name:
APISIX Authorization Code - A monitored user-support email
- Audience: Internal for one Google Workspace or Cloud Identity organization, or External for Google Accounts outside that organization
- A monitored developer-contact email
Accept the Google API Services User Data Policy and create the configuration. For an External app in Testing status, open Audience → Test users and add the account that will validate this guide.
This guide requests only openid, profile, and email. Before production, review the audience, branding, publishing, and verification requirements for your organization and requested scopes.
Expected: the app has the intended Internal or External audience, and the validation account is eligible to authorize it.
What changed: the project has a consent-screen identity and a bounded audience for Google sign-in.
Step 2: Create and capture the OAuth client
Open Google Auth Platform → Clients → Create client, choose Web application, name it APISIX Authorization Code, and add the exact value of ${SSO_CALLBACK_URL}—http://localhost:9080/anything/user/callback for this guide—under Authorized redirect URIs. Leave Authorized JavaScript origins empty because APISIX performs the server-side code exchange.
Create the client, then copy the client ID and client secret immediately.
Google shows and allows download of a new OAuth client secret only when it is created. Store it in a secret manager; if it is lost or exposed, create a replacement instead of placing it in source control or logs.
Copy the values into the shell and generate the APISIX session secret:
read -rp "Google OAuth client ID: " GOOGLE_CLIENT_ID && export GOOGLE_CLIENT_ID
read -rsp "Google OAuth client secret: " GOOGLE_CLIENT_SECRET && export GOOGLE_CLIENT_SECRET && echo
export GOOGLE_DISCOVERY="https://accounts.google.com/.well-known/openid-configuration"
APISIX_SESSION_SECRET="$(openssl rand -hex 32)" && export APISIX_SESSION_SECRETConfirm that the discovery document advertises authorization and token endpoints:
curl -sS "${GOOGLE_DISCOVERY}" | jq -er '.authorization_endpoint, .token_endpoint'Expected: two Google HTTPS URLs.
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/google-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": "${GOOGLE_CLIENT_ID}",
"client_secret": "${GOOGLE_CLIENT_SECRET}",
"discovery": "${GOOGLE_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": "google-sso", "uri": "/anything/user/*", "plugins": { "openid-connect": { "bearer_only": false, "use_pkce": true, … } }, … } }What changed: /anything/user/* now requires a Google-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 Google. Choose or sign in to an account in the configured audience and review the requested profile information.
Expected: Google 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 Google sign-in prompt.
Roll back / Clean up
Delete the APISIX route before deleting its Google OAuth client:
curl -s -X DELETE "${APISIX_ADMIN_URL}/apisix/admin/routes/google-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.
Delete APISIX Authorization Code under Google Auth Platform → Clients only if no other environment uses it. Remove guide-only test users or delete an isolated test project when appropriate. Close the private browser window and unset the secrets:
unset GOOGLE_CLIENT_SECRET APISIX_SESSION_SECRET APISIX_ADMIN_KEYTroubleshoot
| Symptom | Cause | Fix |
|---|---|---|
Google returns redirect_uri_mismatch. | The authorized redirect URI and APISIX redirect_uri differ by scheme, host, port, or path. | Copy ${SSO_CALLBACK_URL} exactly into Authorized redirect URIs and recreate the route if its value changed. |
Google returns access_denied or blocks the account. | The account is outside an Internal audience or is not eligible for an External app under its current publishing policy. | Confirm the audience and add the account as a test user when required; do not broaden production access just to bypass the policy. |
| APISIX cannot redeem the authorization code. | The client secret is wrong, unavailable, or replaced. | Create a replacement secret if needed, update ${GOOGLE_CLIENT_SECRET}, and recreate the route. |
| 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.- Google Auth Platform — Google's current branding, audience, clients, and data-access navigation.
- Manage Google OAuth clients — Google's current web-client and secret-handling guidance.