How Keycloak authorization services work behind Apache APISIX
If your users already sign in through Keycloak, the next question is usually not who the user is but what they may do on a given route. The authz-keycloak plugin answers that at the gateway by sending each request's resource and scope to Keycloak Authorization Services, which evaluates the policies you defined in the realm and returns a grant or a denial. The openid-connect plugin handles a different job: it runs the browser login or validates the bearer token and gives APISIX the identity context. Treat these as two layers. Login establishes who the caller is, and the authorization check decides what that caller can reach. The authz-keycloak plugin is the supported method for this topic, so your design should assume Keycloak resources, scopes and policies are the source of truth for API decisions.
Before building anything, confirm the exact configuration keys and runtime behavior in the authz-keycloak plugin documentation for the APISIX version you will run. The APISIX 3.19.0 release is dated September 28, 2026, and plugin option names, token handling and how denials are returned can change between versions, so do not copy settings from older examples. Ask your implementation team to record the APISIX version, the Keycloak version and the plugin page version in your design document, and to test each of them together in a non-production environment. Pin the Keycloak release you have validated, since Keycloak minor releases can change behavior even when the project keeps breaking changes opt-in.
The main design decisions sit in the Keycloak model, not in APISIX. Decide which resources map to routes or API operations, which scopes describe actions such as read or approve, and which policies (roles, groups, client or user rules) grant them. Keep the resource count manageable, because every additional resource is one more thing to test and audit. Decide also whether the gateway should enforce decisions or only observe them during rollout, and confirm how the plugin's enforcing and permissive modes behave in your version before relying on either. Finally, confirm where the Keycloak evaluation endpoint sits relative to the gateway, since its latency and availability become part of every protected request.
What you need before wiring Keycloak policies to APISIX routes
Fine-grained authorization with Keycloak behind APISIX depends on five pieces being in place before any policy is written. Each card below is a prerequisite that the later configuration relies on, and each names the check to complete first.
A running APISIX deployment
Run APISIX in traditional or decoupled mode, so the Admin API is available on port 9180 for operators while client traffic uses port 9080. Confirm that operators can reach the Admin API from a management network only, and that the etcd cluster behind it is healthy. The authz-keycloak plugin is configured through routes, so route changes must be possible without a restart. Confirm the APISIX version you run against the plugin's documented compatibility.
A Keycloak server on a supported database
Run Keycloak 26.8.1, published October 13, 2026, with a production database from Keycloak's supported list, such as PostgreSQL 14 to 18. The default development file database is not suitable here. Check that the server is configured for production and that the database replication design matches the high availability model you chose, since both supported multi-node designs need a synchronously replicated database.
A realm with an authorization-enabled client
Create a dedicated realm for the APIs and a confidential client for the gateway. Turn on Authorization on that client so that resources, scopes and policies can be defined in Keycloak Authorization Services. Keep browser login clients separate from the gateway client, so that the client secret and authorization settings are not shared. Record the realm issuer URL and client ID, since the plugin configuration needs both exactly as Keycloak reports them.
A decided route inventory
Before writing any policy, list every APISIX route that needs protection, its HTTP methods, its upstream, and the Keycloak resource that will represent it. Mark which routes need only authentication and which need a scope or permission check. An illustrative inventory might map GET /orders and POST /orders to one resource with separate scopes. Routes without a decided owner tend to be left in permissive mode by mistake, so settle this list first.
Certificates for every hop
Plan TLS certificates for three connections: client to gateway, gateway to Keycloak, and gateway to upstream services. Decide which certificate authority issues them, how trust is distributed to the APISIX nodes, and who renews them. Keycloak's token and policy endpoints must be reached over verified HTTPS, so a self-signed or expired certificate on the Keycloak side will break every authorization decision. Confirm the trust store configuration with your security team before testing.
A version compatibility check
Before configuration, confirm the APISIX release and the Keycloak release you run against the plugin's documented compatibility and the Keycloak release notes. Keycloak minor releases can include breaking changes that are opt-in, so pin versions and review the upgrading guide for each target. Record the chosen versions in your change record so that a later upgrade of either product is tested against the same authorization flow.
Choosing enforcing or permissive mode for each route
A route that already serves traffic is the riskiest place to switch straight to enforcement. Any caller whose token lacks the required scope or permission will begin receiving denials at once. Start in permissive mode so the gateway records what would have been denied without blocking anyone, then decide when the evidence supports enforcement.
- Inventory the callers first: list the client IDs, service accounts and user groups that reached the route during a representative period, using access logs and Keycloak event records.
- Collect would-be denials: record the request path, method, client ID, requested scope and the policy that failed, and group them by consumer.
- Close the gaps in Keycloak: grant missing scopes or assign permissions to clients that legitimately need them before the switch.
- Agree an exit condition in writing: define what counts as clean, such as no unexplained would-be denials across a window the API owner and security owner choose.
Sign-off usually rests with the API owner and the security owner, with the platform team carrying out the change under a recorded change request. Confirm the permissive option name and what it logs in the authz-keycloak plugin documentation before you depend on it.
When no existing clients depend on a route, enforcing from the first deployment is often the simpler choice. There is no baseline of unexplained traffic to protect, so the main work moves to testing the policy design before any consumer arrives. Validate the resources, scopes and policies in a non-production APISIX instance connected to a test realm, then promote the same configuration.
- Test each intended client type: issue tokens for every planned client and user role, and confirm that the allowed and denied outcomes match the policy design.
- Test the negative cases: call the route with a valid token that lacks the scope, an expired token and a token issued by a different realm or client.
- Use permissive mode only for a defined onboarding window: if partner or internal consumers are still being prepared, keep permissive mode for a set period and remove it before go-live.
- Version the configuration: store the Keycloak resource and policy definitions alongside the APISIX route configuration so the enforced state can be reproduced and reviewed.
Sign-off usually sits with the API owner, the team that owns the Keycloak realm and a security reviewer of the policy design. Check how clients receive denial responses, because developers will rely on those messages when they debug integrations.
Some routes serve clients that cannot yet obtain the required scopes, such as older integrations or partner systems. A single route-level switch then either breaks those clients or leaves the route open to everyone. Reduce the exposure by separating compliant traffic from non-compliant traffic where the architecture allows it, and by recording each exception with an owner and an end condition.
- Identify every outside client: list each client that cannot present the required scope, with its owner, the reason it is outside policy and the date the owner expects to fix it.
- Separate routes or paths where possible: place compliant and non-compliant consumers on different routes or paths so enforcement applies only where policy is ready.
- Document each exception with a removal condition: each exception needs a named owner, a compensating control such as network restriction or rate limiting, and a condition for removal.
- Keep counting would-be denials: continue to measure denials per client so the exception list shrinks over time, and raise an alert when a client appears that is not on the list.
Sign-off usually involves the security owner approving each exception, the API owner approving any route split, and a named risk owner accepting the residual exposure in writing. Confirm in the plugin documentation whether the mode applies per route or per plugin instance before you design the split.
How to configure Keycloak resources, scopes and policies for APISIX
This sequence takes a Keycloak realm from a confidential gateway client to fine-grained API permissions enforced by the authz-keycloak plugin in APISIX. Each step ends with a check so the team can confirm the layer works before moving to the next one.
Create the realm and gateway client
The Keycloak administrator creates or confirms a realm for the APIs, then creates a confidential OpenID Connect client that APISIX will use to talk to Keycloak. Set the client authentication option so Keycloak issues a client secret, and record the redirect URI for any browser login route. Store the secret in your secrets manager rather than in plain route configuration. The step is done when the client shows a client secret under its credentials tab and a token request from the gateway host returns a valid token for a test user. The exact screens are described in the Keycloak server administration guide, and the client keys used by APISIX are listed in the APISIX openid-connect plugin documentation.
Enable authorization on the client
On the same client, the administrator turns on Authorization so Keycloak creates the Authorization tab with resources, scopes, policies and permissions. Enabling it changes how the client's tokens can carry permission data, so confirm this is done on the client the gateway uses and not on a client shared with unrelated applications. The check is that the Authorization tab opens and shows a default resource and default policy. The Keycloak server administration guide covers the Authorization settings screen for the version you run.
Define resources and scopes for the APIs
The API owner lists each protected resource as a Keycloak resource, using route paths or a named group of routes that match how APISIX is configured. Then the owner defines scopes for the actions that matter, typically read, write and admin, and attaches the scopes to each resource. Keep the resource names aligned with the APISIX route names so that logs and policy reviews can be traced to the same object. The check is a resource list in which every production route has at least one resource and each resource lists the scopes it accepts.
Create policies and combine them into permissions
The administrator creates policies based on realm roles, client roles, groups or specific users, depending on how your identity model is organized. Each policy is then referenced by a permission that links a resource and scope set to the policy that is allowed to use them. Keep the number of policies small and name them after business roles so that reviewers can read the rules. The check is the Evaluate tab in the client's Authorization section: a test user with the expected role should receive a grant for the intended scope and a denial for a scope they should not hold.
Configure openid-connect for browser and bearer flows
On the APISIX route, the engineer configures the openid-connect plugin with the realm discovery URL, the client identifier and the client secret reference. Browser routes use the authorization code flow with PKCE, while API clients that already hold a token use bearer validation through introspection or the JWKS endpoint, as set by the plugin's options. Confirm the discovery URL, the issuer and the audience values against the realm before saving. The check is that an unauthenticated request to a protected route is redirected or rejected as expected, and that a request with a valid token reaches the upstream. The exact keys for each flow are in the openid-connect plugin documentation.
Add authz-keycloak in permissive mode
The engineer adds the authz-keycloak plugin to the selected routes with permissive mode enabled, so the gateway calls Keycloak for decisions and logs the result but still forwards requests. This lets the team see which requests would be denied without breaking traffic. The plugin reads the same client identifier and secret as the openid-connect configuration, so keep them consistent. The check is that the gateway logs contain permission decisions for test requests and the upstream still receives every request that the team expects to pass.
Test with tokens for each role
The QA or security engineer obtains tokens for a test user in each role, including a user with no role, and sends requests to every route and method in the permissive configuration. The team compares the logged decisions with the intended matrix of roles, resources and scopes and records any mismatch as a policy defect. Repeat the tests after each policy change. The check is a signed-off matrix in which every role and route combination matches the expected allow or deny result. Keep the test users and their roles documented so the same tests can be rerun during upgrades.
Switch selected routes to enforcing mode
After the matrix passes, the engineer changes the authz-keycloak setting on selected routes from permissive to enforcing, starting with lower-risk routes and then the higher-risk ones. Denied requests should now return an error at the gateway instead of reaching the upstream, so the rollout should be scheduled with the application owners and a rollback path ready. The check is that the test matrix still passes with enforcing mode on, and the gateway logs show denials only for requests the matrix expected to fail. Confirm the exact enforcing option name and its behavior in the current authz-keycloak plugin documentation before the change.
How Keycloak resources, scopes and roles map to APISIX routes
This table shows one way to line up APISIX routes with Keycloak resources, scopes, policies and the token claims each decision reads. Use it as a starting point, then write your own mapping before the build begins so that every route has a named owner, a resource and an expected token shape. The main takeaway is that most authorization failures come from mismatched names and claims, not from the gateway itself.
| APISIX route or path | Keycloak resource | Scope and policy type | Token claim or role the decision reads |
|---|---|---|---|
| Route /orders/* (illustrative) | Resource named orders, one resource per business object | Role-based policy on the orders scopes | realm_access.roles or resource_access roles for the client |
| GET and HEAD on /orders/{id} | orders resource | Scope orders:read | scope claim in the access token |
| POST, PUT and PATCH on /orders/{id} | orders resource | Scope orders:write | scope claim in the access token |
| DELETE on /orders/{id} | orders resource | Scope orders:delete with a narrower role policy | Role claim from the client's resource_access block |
| Route /reports/* shared by two teams | reports resource with two named scopes | Group-based policy per team | groups claim, if the group mapper is enabled in the client |
| Routes that call a downstream service | Service-account client, not a user resource | Client credentials grant with a scope limited to the service | azp or client_id claim and the scope claim |
| Any route accepting the token | Resource server client ID as the expected audience | Audience check before policy evaluation | aud claim must include the API client ID (confirm in your Keycloak version) |
| Admin or internal paths | Separate resource with its own owner | Deny by default unless a policy matches | Role claim plus source network or client, confirmed with your security team |
How the openid-connect login and the authorization check fit together
Authentication and authorization are separate decisions, and this section matters because a gateway can get the first right while the second is never configured. The openid-connect plugin answers who the caller is. Browser users go through the authorization code flow with PKCE, which ends with APISIX holding a session for the user. Services and machine clients typically send a bearer token, which APISIX validates, or they obtain tokens through the client credentials grant. Token introspection asks the identity provider whether a presented token is still active, which matters when you need revocation to take effect quickly. None of these steps tells APISIX whether this particular user may call this particular route with this HTTP method.
The authorization check is a second decision made after authentication succeeds. With authz-keycloak, APISIX sends the request to Keycloak Authorization Services, where resources, scopes and policies are evaluated against the token. A token that is valid, unexpired and issued by the right realm can still be denied for a route because no permission grants the requested scope to that client or user. This is the most common surprise during rollout, so test a denied case for every protected route, not only a successful login. Confirm in the plugin documentation which token and permission fields your APISIX version forwards to Keycloak before you write the resource definitions.
Plan the two layers together. Decide which clients use authorization code with PKCE, which use client credentials, and which accept bearer tokens from other systems, then map each client to the scopes and resources it needs. Keep token lifetimes and introspection settings under review with your identity team, since those values control how long a revoked permission can still be honored. Ask DeployOpen or your own engineers to write one allow case and one deny case per route in your test plan before enforcement is switched on.
Hardening token handling and the Keycloak administration path
The APISIX and Keycloak pairing puts two administrative surfaces and a stream of tokens on the same request path. The cards below cover the settings that connect them: where client secrets live, who can change authorization policy, how tokens expire and get revoked, and what evidence you keep. Confirm each setting against the Keycloak production and metrics documentation and your own APISIX configuration before go-live.
Client secrets and where they live
The openid-connect and authz-keycloak plugins need a client ID and secret for each Keycloak client. Treat that secret as credential material: keep it out of route definitions that are copied into tickets, repositories or shared dashboards. Confirm with the APISIX documentation exactly where the plugin stores the secret in etcd or the standalone file, whether it is encrypted at rest, and whether a secret reference from an external store is supported in your APISIX version. Rotate the secret in Keycloak and in the gateway together.
Limiting who edits authorization policy
Resource, scope and policy changes in Keycloak alter who can call which API, so they carry the same weight as a firewall change. Restrict realm administration to a small named group, use separate realms or clients for production and non-production, and require a reviewed change for every permission edit. Keep the authorization model in version-controlled exports where your process allows it, so a reviewer can see what changed and when.
Restricting the Admin API and console
The APISIX Admin API on port 9180 in traditional mode changes routes and plugin settings, including authorization rules, so it must not be reachable from client networks. Keep it on an internal interface, protected by an admin key you rotate. Apply the same discipline to the Keycloak admin console: place it behind a separate network path or access proxy, restrict it to administrator addresses, and confirm the production hostname settings in Keycloak before go-live.
TLS on every hop
Tokens travel from the browser to APISIX, from APISIX to Keycloak for discovery, introspection or token exchange, and from APISIX to upstream services. Each hop needs TLS with certificate validation enabled, including the connection from the gateway to the Keycloak token and JWKS endpoints. Confirm how the plugin verifies certificates for backchannel calls, and whether your internal certificate authority is trusted by the gateway process. Plain HTTP on any hop can expose bearer tokens.
Short token lifetimes and revocation
Access tokens are the credential the gateway accepts, so their lifetime sets how long a revoked user or changed permission stays effective. Choose access token lifetimes that match your risk, keep refresh tokens shorter for sensitive APIs, and test how a disabled user or removed role takes effect at the gateway. Introspection checks against Keycloak reflect revocation sooner than local JWT verification, at the cost of a call per request. Document the expected delay so application owners know what to expect.
Audit logging of permission decisions
Authorization decisions made at the gateway need evidence: which principal, which route, which scope, and whether the request was allowed or denied. Enable access logging in APISIX with the fields you need, and confirm which of the authorization outcomes the plugin records and at what level. Also keep Keycloak admin and login events, so you can tie a permission change to the person who made it. Make sure logs do not capture full tokens or client secrets.
Keycloak metrics for monitoring
Keycloak exposes metrics that show login volume, token activity and health, which help you spot failed logins, unusual token issuance or an unhealthy node before users report it. Decide which metrics to scrape, enable the endpoint only on an internal network, and set alerts for sustained error rates and unavailability of the token endpoint. A gateway that cannot reach Keycloak will deny or fail requests, so monitor that dependency directly.
Failures to expect: unexpected 403s, stale permissions and token problems
This table pairs each failure you are likely to meet with its usual cause and the check your team should run first. Read it as a triage list: match the symptom you see, confirm the cause with the named check, then apply the fix. Most failures trace back to the token contents, the policy evaluation path, or the gateway's ability to reach Keycloak.
| Symptom | Likely cause | Check and fix |
|---|---|---|
| Every request is denied with 403 after enabling enforcement | Enforcement mode is on before the resource, scope and policy mappings match the routes in use | Switch the route back to permissive mode, then compare each route's resource and scope names in Keycloak with the names the plugin sends. Correct the mismatch, re-test with a known token, and only then re-enable enforcement. |
| Permissions do not change after a Keycloak policy edit | The gateway is working from a cached decision or a token issued before the change | Confirm the change in the Keycloak admin console, then request a new token and test again. Short token lifetimes and the time the decision is cached determine how long the old result persists, so record both values before go-live. |
| Token lacks expected roles or scopes | A protocol mapper is missing, or the client scope that carries the claim is not assigned to the client | Decode a sample access token and check the claims the policy reads. Add or fix the mapper, confirm the client scope is a default or requested scope, and check that the audience value matches the client the gateway expects. |
| Token rejected as not yet valid or expired immediately after issue | Clock skew between the Keycloak host, the APISIX nodes and the client | Compare system time on all three with NTP status. Correct time synchronization on the hosts first. Do not widen the accepted skew in the gateway until the clocks are confirmed to agree. |
| Gateway returns 500 or times out on authorization or token requests | APISIX cannot reach the Keycloak discovery document or token endpoint, often because of DNS, TLS trust or egress rules | From a APISIX node, fetch the realm's OpenID configuration URL and confirm the issuer and endpoint values match what the plugin is set to. Check the CA certificate used for TLS and the network path, then retest. |
| Behavior changes after a Keycloak upgrade | A minor or major release changed defaults, deprecated a setting, or altered schema migration or token behavior | Read the release notes and backwards compatibility guidance for the target version before upgrading. Run the authorization test suite against a staging realm restored from production, and keep the operator in manual approval mode so upgrades are scheduled. |
What does Apache APISIX Keycloak authorization take, and can our team do it alone?
What actually drives the effort of an APISIX and Keycloak authorization project?
The effort follows the number of routes, resources, scopes and policies that need protecting, along with how the existing Keycloak realm is designed. Each route that carries a distinct permission model adds mapping work, and each resource needs a clear owner and a defined set of scopes. Token claim mapping matters because APISIX decisions depend on what Keycloak puts in the token. Testing depth is the last major driver: a small set of allow and deny cases differs greatly from a full matrix of identities, roles and routes run in both enforcing and permissive modes. Ask any provider to break its quote down along these four drivers so you can see which ones you control.
What costs are one-time and what recurs after go-live?
The one-time work covers discovery, the route inventory, Keycloak resource and policy design, the APISIX configuration, test cases and the cutover from permissive to enforcing mode. Recurring work is policy maintenance as new APIs and roles appear, plus upgrades. APISIX releases and Keycloak releases both need regression testing against your authorization rules. Keycloak plans four minor releases a year, and its Operator documentation strongly recommends manual approval for operator updates, so budget for a review step each time. Ask the provider whether ongoing support is quoted separately as a retainer or included in the fixed scope.
What does our team need to provide before the work starts?
Four inputs make the engagement workable. First, named owners for the APIs, the Keycloak realm and the APISIX gateway, with authority to approve policy decisions. Second, a route inventory listing each upstream, its methods, its current authentication and the business meaning of its resources. Third, test identities covering each role and group that should reach or be denied each route, in non-production realms. Fourth, a change window for the switch from permissive to enforcing mode, with a rollback plan. Missing any of these usually pushes work into discovery and extends the timeline, so confirm them before the quote is finalized.
Can our internal team set up APISIX and Keycloak authorization without outside help?
Yes, if the team already has working knowledge of both products and enough time to run the process properly. The skills needed include Keycloak realms, clients, scopes and authorization services, APISIX routes and plugin configuration, and OpenID Connect token behavior. The most commonly underestimated work is evidence gathering in permissive mode, where requests are logged and evaluated without being blocked so the team can see what enforcement would change. Internal teams often find that this phase, plus the test matrix, takes more calendar time than the configuration itself. A partner can cover part of this work or work alongside your team, depending on the scope agreed in writing.
How long does an APISIX and Keycloak authorization project take?
The duration depends on the scope, and no fixed timeline applies to every organization. A project with a few routes and an existing, well-structured Keycloak realm will move far faster than one covering dozens of APIs with overlapping roles and legacy clients. Schedule risk usually sits in realm design decisions, claim mapping and the permissive-mode review. DeployOpen works from a written scope with agreed deliverables and timelines, so the duration is stated in the quote once the route inventory and realm details are known. Ask for the phases, the dependencies on your teams and the review points in any proposal you receive.
Does DeployOpen issue audit reports for our authorization controls?
No. DeployOpen does not issue audit reports, attestations or certifications. It prepares systems, controls and evidence so that independent auditors and certification bodies can assess them. For authorization, that evidence can include the documented resource and policy model, the test cases and their results, the permissive-mode review records, change approvals and the configuration as it stands at go-live. Your assessor decides what is sufficient for the framework in scope. Agree early with your auditor on which evidence they expect, so the project records it from the start.
Which parts of the work do we need to confirm with the vendors themselves?
Several details depend on the exact versions in your environment, so confirm them rather than assume them. Check that your APISIX version supports the authz-keycloak plugin behavior you plan to use, and review its upgrade notes for any changes in the release you run. Confirm your Keycloak version, its supported database and your high availability design, since those affect how token and policy data is served. Also confirm how your Keycloak Operator update approval is set up. The upstream project documentation and your Red Hat or vendor support terms are the right places to check these points before design begins.
How DeployOpen scopes, delivers and supports this integration
DeployOpen plans and implements Apache APISIX and Keycloak authorization inside your own infrastructure, with the scope agreed in writing before work starts. We build the route, token and policy design together with your identity and platform teams, then hand over documented configuration, tests and runbooks.
How we scope
- Inventory the APIs, routes and upstreams that need fine-grained authorization at the gateway.
- Map Keycloak realms, clients, resources, scopes and policies to the access model your teams already use.
- Agree in writing whether APISIX runs in enforcing or permissive mode during each rollout phase.
How we build
- Configure openid-connect login for browser-facing apps and token checks for API callers.
- Wire the authz-keycloak plugin to Keycloak resource and scope policies, then test allow and deny paths per route.
- Store client secrets and Keycloak endpoints through your chosen secret handling, not in plain route configuration.
How we hand over
- Deliver the APISIX configuration, Keycloak authorization model and an allow and deny test matrix.
- Document token grant types, scope names and the procedure for adding a new protected resource.
- Walk your platform and identity teams through the runbook for changes, rollback and permissive-mode exits.
How we support
- Ongoing support, upgrades and continuous testing are available as retainers, scoped separately from the build.
- Revisit policies when APISIX or Keycloak releases change plugin behavior or Keycloak upgrade procedures.
- Confirm support terms, response expectations and included upgrade work with the written proposal before signing.

