Protecting web applications with APISIX openid-connect login: when the gateway should own sign-in
Choose the gateway as the place where sign-in is enforced when several internal web applications need browser login and you do not want each application team to build and maintain its own login code. With the openid-connect plugin set to bearer_only false, APISIX runs the OpenID Connect authorization code flow for browser requests. An unauthenticated user is redirected to your identity provider, returns with an authorization code, and APISIX exchanges that code for tokens and stores the session in an encrypted cookie. The upstream application then receives requests only after sign-in succeeds, and it can keep its own authorization logic. This model fits legacy or third-party applications that cannot easily change, and it gives security teams one place to set session, logout and redirect behavior.
The plugin works with several identity providers, including Keycloak, Auth0, Microsoft Entra ID, Google and Okta, so the choice of provider is usually driven by your existing directory and contracts rather than by the gateway. Compare this with the alternatives before committing. App-level login keeps identity logic inside each application, which suits applications that need fine-grained user context. Forward-auth delegates each decision to a separate service, which suits teams that already run a central auth service. The gateway-owned login is usually the better fit when you want one enforcement point and the applications can trust identity headers passed from the gateway. Confirm that your applications can accept those headers and that no path bypasses the gateway.
Before you design the build, confirm three things with your identity team and in the APISIX openid-connect plugin documentation: which identity provider and client registration you will use, how the session secret will be generated and stored, and which deployment mode (traditional, decoupled or standalone) your gateway will run in. Those answers determine the redirect URIs, the logout path and the verification steps your team must plan for. Ask a partner to confirm how these settings behave in your own environment before go-live.
Which openid-connect settings fit which login pattern
This table compares the openid-connect plugin settings that shape a browser login in front of an internal app, with the pattern each one fits and what breaks when it is wrong. Read each row against the login model you intend to run, then check the default and the failure column before you accept a configuration.
| Setting | When it fits | What breaks if it is wrong |
|---|---|---|
| bearer_only set to false | Browser users sign in through the identity provider and APISIX runs the authorization code flow with a session cookie. | If left true, APISIX expects a bearer token and browser users receive errors instead of a login redirect. |
| session.secret | Any browser login where APISIX encrypts the session cookie. Generate a random value of at least 16 characters and store it in your secrets system. | A secret shorter than 16 characters is rejected. A secret that leaks lets someone forge or decrypt sessions, and rotating it signs out all current users. |
| use_pkce | Public or browser-facing clients where the authorization code could be intercepted, and any client your identity provider expects to use PKCE. | Without PKCE, a provider that requires it rejects the login. With it off on a client that does not need it, you still get a working login, but you lose a protection you may have assumed. |
| Logout path (/logout) | Apps that need users to end the APISIX session as well as the identity provider session, using the default path or a path you set. | Users who only close the browser may remain signed in at the gateway. Links to a path the plugin does not handle leave the session active. |
| ssl_verify | Production deployments where APISIX calls the identity provider over HTTPS with certificates signed by a trusted or internal CA that is installed on the gateway. | The default is true since APISIX 3.16.0, so older configs that relied on the former false default fail on untrusted certificates. Disabling it removes server identity checks on token and discovery calls. |
| required_scopes | Apps that should admit only users granted a specific scope, such as a role-based access scope from your identity provider. | Requests without the required scopes receive 403. A missing scope in the identity provider mapping blocks users who should have access, so confirm scope mapping before rollout. |
| claim_validator (issuer and audience) | Any deployment where tokens must come from your identity provider and be meant for this application client. | Without issuer and audience checks, tokens issued for another application in the same provider may be accepted. Mismatched values usually show up as failed logins or 401 responses. |
| Identity headers to upstream | Apps that read the user name, email or groups from request headers and do not run their own login. | If upstream apps trust these headers from any source, clients can spoof them. Strip or overwrite inbound copies at the gateway and confirm the header names with your app team. |
Traditional, decoupled or standalone mode for login routes
In traditional mode, the data plane and control plane run together on the same APISIX nodes, and etcd stores the routes and plugin configuration. You create the route with the openid-connect plugin through the Admin API, and APISIX serves client traffic on port 9080 while the Admin API listens on port 9180. The plugin settings, including the session.secret and the identity provider client details, are applied to the route and take effect without a restart.
- Client traffic: Browsers reach the login route on port 9080, so the redirect URI registered with your identity provider must match that public address.
- Admin API: Port 9180 carries configuration changes. Restrict it to automation and administrators, and keep it off public networks.
- Secret placement: Store the client secret and session secret in a secrets manager or environment reference rather than in exported route files.
- Change path: Route updates go through the Admin API and etcd, so a change to the login route is a configuration write that you should be able to audit and roll back.
Check that the redirect URI, the Admin API exposure and the stored secrets all match your access model before you enable the route for users.
In decoupled mode, the data plane nodes that receive browser traffic are separate from the control plane that manages configuration, and both share one etcd cluster. The openid-connect settings are still defined as route or plugin configuration, but the data plane only reads them. This split lets you place login-enabled gateways in a network zone that is not allowed to reach the management interfaces.
- Separation of duties: Administrators change login routes on the control plane, while data plane nodes serve user sessions.
- Shared etcd dependency: Both planes depend on the same etcd cluster, so its availability and access controls determine whether route changes reach the gateway.
- Network placement: Data plane nodes need outbound reachability to the identity provider for discovery, token exchange and JWKS retrieval, and that path should be confirmed for each zone.
- Session continuity: Because sessions are held in encrypted cookies, a data plane node can validate a session without calling the control plane, provided the same
session.secretis configured on every node.
Confirm the exact deployment steps for your APISIX version with the official deployment modes documentation before you split the planes.
Standalone mode removes etcd entirely. Routes and plugin settings are defined in conf/apisix.yaml or conf/apisix.json, or pushed through the API. In file-driven standalone mode, APISIX checks the file for changes every second and hot-reloads the configuration without restarting. This suits environments where you want configuration to live in version control and deploy through a pipeline.
- File checks: APISIX reads
conf/apisix.yamlorconf/apisix.jsonevery second, so a valid edit takes effect quickly without a restart. - Version control: Keep the file in Git, review changes to the openid-connect block, and roll back by reverting the commit.
- Secret handling: The file holds the
client_secretandsession.secretin plain text unless you reference them from an external source, so control who can read the file and the deployment artifacts that contain it. - Validation before rollout: A malformed file can disrupt routing, so validate the YAML or JSON in the pipeline before it reaches the gateway.
In standalone mode there is no Admin API on port 9180 for this configuration path, so confirm which management interfaces your version exposes before you assume the port layout from traditional mode applies.
How to configure APISIX openid-connect login step by step
This sequence takes an internal web app from no login to a gateway-enforced OpenID Connect sign-in with PKCE, a working logout, and identity headers reaching the upstream. Each step ends with a check so you know it held before moving on.
Register a confidential client in your identity provider
The identity team creates an OpenID Connect client for the app in your identity provider, such as Keycloak, Microsoft Entra ID, Okta or Auth0. Set the redirect URI to the gateway callback path for the route's public hostname, enable the authorization code flow, and record the client ID and client secret in your secrets store. Confirm the client shows the expected redirect URI and that the issuer discovery document loads from the browser or a curl request. Ask the identity team which scopes and claims the app needs, because the gateway can only pass what the token contains.
Generate and store the session secret
Generate a random session secret of at least 16 characters, which the openid-connect plugin uses to encrypt the session cookie. Store it in your secrets manager and reference it from the APISIX configuration rather than pasting it into route files that get committed to source control. Confirm the value is long enough and is different in each environment. Any change to this secret invalidates existing sessions, so plan that before rotating it in production.
Define the route and upstream
Create the APISIX route that matches the public hostname and path of the internal app, and point it at the upstream service address. Keep the route simple at this stage, with no authentication plugin, so you can confirm traffic reaches the app. Test with a direct request through the gateway and check that the response matches what the app returns when called directly. This establishes a known-good baseline before you add the login layer.
Set the openid-connect plugin options
Add the openid-connect plugin to the route with bearer_only set to false so the plugin runs the browser login flow. Set the discovery URL or endpoints from the identity provider, the client ID, the client secret reference, the redirect URI, the session secret and the scopes the app requires. Leave ssl_verify at its current default in your APISIX version and confirm the identity provider certificate chain is trusted by the gateway. Check the route configuration is accepted by the Admin API and that the plugin loads without errors in the error log.
Enable PKCE and confirm the authorization request
Set use_pkce to true so the authorization request carries a PKCE challenge in addition to the client secret. Open the app in a private browser window and follow the redirect to the identity provider. Inspect the authorization request and confirm it includes the code challenge parameter and the expected scopes. Your identity provider logs or client settings should show that PKCE was used for the sign-in. If the provider rejects the request, check that the client is allowed to use PKCE before changing the gateway.
Test login and identity headers upstream
Sign in with a test user and confirm the browser lands back on the app through the gateway callback. Check that the app receives the identity headers you configured, and that the app sees the expected user, groups or claims. Decide which token or header values the upstream needs, and avoid forwarding tokens the app does not use. Confirm the app behavior with at least one account that lacks a required scope, which should be refused according to the plugin's scope and claim settings.
Test logout and an unauthenticated request
Send a request with no session cookie and confirm the gateway redirects to the identity provider rather than returning app content. Then call the logout path, which the plugin serves at /logout by default, and confirm the session cookie is cleared and a new request is redirected to sign-in. Also check whether your identity provider session must be ended separately for true single logout, and record the result. Token revocation depends on the provider and the plugin settings, so confirm the exact behavior with your identity team before you rely on it for offboarding.
Security settings that decide whether the login holds
A login in front of an internal app is only as strong as the settings around the openid-connect flow. The cards below cover the session cookie, the PKCE and client settings, what logout really ends, how upstreams trust identity headers, TLS verification and the events you keep for review. Each one needs a named owner and a check before go-live.
Session secret handling and rotation
With bearer_only set to false, the plugin encrypts the session cookie using session.secret, which must be at least 16 characters. Generate it from a secure random source, store it in your secrets manager rather than in route files, and limit who can read the APISIX configuration that holds it. Ask your team how a rotation invalidates existing sessions and whether users are sent back to the identity provider when it happens. Test that rotation works in staging before you need it in production.
PKCE on the authorization code flow
Enabling use_pkce adds a code challenge to the authorization code flow, so an intercepted authorization code cannot be exchanged by another client. Confirm that your identity provider client is configured for the same flow and accepts the PKCE method APISIX sends. Mismatched client settings usually show up as failed logins rather than as warnings, so include a login test in your acceptance checks for each environment.
Logout and token revocation limits
The plugin serves logout at /logout by default, which clears the local session. That does not, by itself, revoke tokens already issued by the identity provider or end sessions held at other applications. Ask the identity provider team whether front-channel or back-channel logout is available and which token lifetimes apply. Document the gap between local logout and full session end so application owners do not assume more is revoked than actually is.
Header trust so upstreams accept only APISIX identity
Once APISIX passes identity data to the app, the app must trust only requests that came through the gateway. Any client that can reach the upstream directly can forge headers such as user or group names. Restrict network paths so the upstream accepts traffic only from APISIX, strip identity headers arriving from outside, and confirm which claims are forwarded. Test by sending a request straight to the upstream with spoofed headers and checking that it is refused.
TLS verification with ssl_verify
The ssl_verify setting controls whether APISIX checks the identity provider's certificate during discovery and token exchange. The default changed from false to true in APISIX 3.16.0, so check the value in any configuration copied from older examples. Confirm that the CA chain for the identity provider is trusted by the APISIX host, and avoid disabling verification to get past a certificate error. Fix the trust store instead.
Logging of authentication events
Login successes, failures, logouts and token errors are the events your security team needs for investigations and audit evidence. Decide which fields APISIX writes to its access and error logs, where they are shipped, how long they are kept and who can read them. Make sure logs never contain session cookies, authorization codes, tokens or the session secret. Agree a sample of expected events with your SOC and check that they appear in the central store.
Passing identity headers upstream without trusting the browser
After the openid-connect plugin completes the authorization code flow, it can forward identity information to the upstream application as request headers, such as user name, email or groups, depending on how you configure the plugin. The upstream app then trusts those headers to decide who the user is. That trust is only safe if the gateway is the only component able to set them. A browser can send any header it likes, so an upstream that reads an identity header without checking where it came from can be impersonated by any user who adds the same header name to a request. The gateway must overwrite or strip these header names on every request, including requests that never pass through the login flow.
Before go-live, confirm three things with the application owner. First, list the exact header names each app reads, because the plugin's header settings and the app's expectations must match character for character. Second, confirm which claims the plugin maps into those headers in your APISIX version by checking the openid-connect plugin documentation, rather than assuming a default set. Third, decide whether the upstream receives the access token or ID token at all. Passing tokens upstream increases what a compromised app can replay, so forward only what the app needs. Also verify that the app is reachable only through APISIX, since a direct network path to the upstream bypasses every header rule at the gateway.
Test the trust boundary explicitly. Send a request through the gateway with a forged copy of each identity header while logged out and while logged in, and confirm the upstream sees only the values APISIX set. Repeat the test on every route that reaches the same upstream, including routes that do not require login. This work overlaps with the header trust controls in the APISIX security hardening group, so keep the header rules in one reviewed place. Treat the header list as part of the change record: any new header or upstream must go through the same check before release.
Limits to confirm before rollout
The openid-connect plugin handles the browser login flow, but several behaviors depend on your identity provider, your APISIX version and your traffic pattern. Confirm the items below in a staging environment before any internal application goes behind the gateway.
Logout and token revocation
APISIX clears its own session at /logout by default, but that does not by itself end the user's session at the identity provider or revoke tokens already issued. Ask your identity provider team which logout endpoint your realm or tenant exposes, whether back-channel logout is available, and how long an access token stays valid after a user is disabled. Test a logout followed by a browser back navigation to confirm the protected page refuses access.
Introspection or local token checks
Decide whether APISIX validates access tokens by calling the identity provider's introspection endpoint or by checking signatures locally against published keys. Introspection gives revocation sooner but adds a network dependency on every request or cache window, while local checks are faster but keep accepting a token until it expires. Confirm which mode your configuration uses and what happens to requests when the identity provider is unreachable.
Session storage under load
Session data sits in the encrypted cookie that the plugin manages, so the gateway holds no shared session store that you must size. Confirm that session.secret is at least 16 characters, stored in your secret manager, and identical across every APISIX node that serves the same application. Load test concurrent requests from one user, since parallel token refreshes can collide and send the user back to login.
Provider support in your version
The plugin works with providers such as Keycloak, Auth0, Microsoft Entra ID, Google and Okta, but the exact discovery, claim names and group formats vary by provider and tenant. Check the APISIX release you plan to run against the provider version you operate, and confirm which claims carry roles or groups in your tenant. Record the result in the design so that upgrades on either side trigger a retest.
Scope and claim enforcement
The required_scopes option returns 403 when a requested scope is missing, and claim_validator checks issuer and audience. Both behave differently from a simple login check, so a token that is valid for another application can fail or pass depending on how these options are set. Confirm that your identity provider puts the expected scopes, audience and issuer in tokens for each application, and test one token with a missing scope and one with a wrong audience.
Default TLS verification change
The plugin's ssl_verify default changed from false to true in APISIX 3.16.0. Configurations copied from older examples or from a lower release may behave differently after an upgrade, especially when the identity provider uses a private certificate authority. Confirm the certificate chain your APISIX nodes trust, and review every openid-connect route for an explicit setting before upgrading.
Questions teams ask before protecting web apps with APISIX OIDC
How much effort does protecting a web app with APISIX OIDC take?
Effort depends mostly on how many applications sit behind the gateway, how many identity provider clients you need, and how much session and logout behavior you must control. A single app with one OpenID Connect client is a small change in the gateway configuration. Each additional app adds a client registration, redirect URIs, route-level plugin settings, a session secret decision, and a test pass for login, logout and header handling. Ask any partner to split the estimate into identity provider work, gateway configuration, upstream header changes and testing, because each part has a different owner.
Can our own team set this up without outside help?
A team with gateway administration experience and identity provider administration access can configure a first application using the openid-connect plugin documentation. The harder parts are design decisions: whether APISIX holds the session or a separate proxy does, how the session secret is stored and rotated, how logout reaches the identity provider, and how upstream apps trust forwarded identity headers. Teams that have not run OIDC login before often find these decisions take longer than the configuration itself. A written review of those choices, before rollout, is a reasonable point to bring in outside help.
What do we need to provide before the work starts?
You will need three things ready before configuration begins:
- Administrator access to the identity provider, or a named person who can create clients, redirect URIs, scopes and test users
- Owners for each internal web application, who can confirm callback URLs, the claims the app expects, and how the app handles logout
- Test users with roles that match production access, plus a staging copy of each app and a non-production identity provider realm or tenant
Also confirm who approves changes to the identity provider, because client secrets and redirect URIs are security-sensitive settings.
How long does an OIDC login rollout take?
There is no fixed duration that applies across environments. Timing depends on the number of applications, whether each one already supports OIDC or needs header-based identity, the approval path for identity provider changes, and how many environments must be tested before production. A single app in an environment where the identity provider team responds quickly can move in a short cycle. Several apps with different logout expectations and change windows take longer. Ask for a timeline that lists each application, each approval step and each test stage, and check that the plan states what is excluded.
How does APISIX OIDC compare with OAuth2 Proxy or Keycloak-only login?
APISIX OIDC keeps login at the gateway you already run, so one component handles routing, login redirects and plugin policy. OAuth2 Proxy is a separate reverse proxy or middleware that handles the same browser login pattern, and it adds another component to deploy, patch and monitor. Keycloak-only login means the identity provider handles user sign-in, but each app still needs its own OIDC client code or a proxy in front of it. The right choice depends on where you want session state to live, which components your team already operates, and whether the apps themselves must accept tokens directly. Compare the session storage model, logout behavior and header handling for each option against your own apps before choosing.
What changes when an APISIX upgrade affects ssl_verify defaults?
The openid-connect plugin's ssl_verify default changed from false to true in APISIX 3.16.0. After that version, the plugin verifies the identity provider's TLS certificate unless you set the option otherwise. Teams upgrading from an earlier release may see login or token calls fail if the identity provider uses a private certificate authority that the gateway does not trust. The fix is to add the correct CA bundle to the gateway trust store and keep verification on, rather than turning verification off. Test the upgrade in staging against the real identity provider endpoints, and review any route that sets ssl_verify explicitly.
Which session and logout settings should we review before go-live?
Review the session secret, the cookie settings, the logout path and the token revocation behavior before go-live. The plugin requires a session secret of at least 16 characters when bearer_only is false, and the plugin's logout runs at /logout by default. Confirm that the secret is stored in your secrets system, that PKCE is enabled through use_pkce, and that logout also ends the identity provider session where your policy requires it. Test that a logged-out user cannot reach protected routes with an old cookie, and document how the team rotates the secret without forcing every user to sign in again.
Official documentation to read alongside this page
Read the APISIX openid-connect plugin documentation before you write the route configuration. It defines the behavior this page depends on: how bearer_only switches the plugin into the authorization code flow, the minimum length for session.secret, what use_pkce turns on, where the default /logout path sits, and how ssl_verify and claim_validator behave in your APISIX version. Because the ssl_verify default changed in APISIX 3.16.0, confirm which release your team will run and check that setting against the plugin page for that version before you copy any example configuration.
Read the APISIX deployment modes documentation next, because the choice between traditional, decoupled and standalone mode decides how the OIDC settings reach each gateway node. Standalone mode reloads conf/apisix.yaml or conf/apisix.json every second, so a session secret or client secret stored in those files needs the same protection as any other credential. The page also lists the default client port (9080) and Admin API port (9180) for traditional mode, which your network team will need when they place the gateway behind a load balancer and restrict access to the Admin API.
Keycloak has its own documentation for the decisions that sit on the identity side of this flow. The Keycloak server administration guide covers clients, redirect URIs and sessions, and the Keycloak high availability overview explains why both supported designs need a synchronously replicated database. Confirm with your identity team which Keycloak release and database your environment will run, since session lifetimes and logout behavior depend on those choices, and test back-channel logout and token revocation against the version you deploy.
How DeployOpen scopes and delivers APISIX login protection
DeployOpen plans, builds, tests and documents the APISIX openid-connect login inside your own infrastructure, from the identity provider integration and session design to the upstream header contract. Engagements are fixed-scope, so the sign-in flow, logout behavior and handover items are agreed in writing before any configuration changes begin.
How we scope
- Inventory the internal web apps, their hostnames, routes and which ones need browser login.
- Confirm the identity provider, client registration, redirect URIs and whether bearer_only stays false.
- Agree in writing how sessions, logout, token revocation and identity headers will be handled.
How we build
- Configure the openid-connect plugin with session secret handling, PKCE and the /logout path.
- Set ssl_verify, required_scopes and claim_validator checks for issuer and audience on each route.
- Build standalone or Kubernetes configuration as code, then test login, expiry, logout and denial paths.
How we hand over
- Deliver the route configuration, secret storage approach and an upstream identity header reference.
- Document the verification checklist, test cases and known limits of cookie-based sessions.
- Walk your operations and security teams through change procedures and rollback steps.
How we support
- Support is available as a retainer for upgrades, plugin changes and identity provider updates.
- Continuous security testing of the login flow can be added to the retainer where agreed.
- Confirm response times, upgrade windows and included hours with us before the retainer starts.

