Skip to main content
Version: 2.10

authz-keycloak

Summary#

Name#

authz-keycloak is an authorization plugin to be used with the Keycloak Identity Server. Keycloak is an OAuth/OIDC and UMA compliant Identity Server. Although, it's developed working in conjunction with Keycloak it should work with any OAuth/OIDC and UMA compliant identity providers as well.

For more information on Keycloak, refer to Keycloak Authorization Docs for more information.

Attributes#

NameTypeRequirementDefaultValidDescription
discoverystringoptionalhttps://host.domain/auth/realms/foo/.well-known/uma2-configurationURL to discovery document for Keycloak Authorization Services.
token_endpointstringoptionalhttps://host.domain/auth/realms/foo/protocol/openid-connect/tokenA OAuth2-compliant Token Endpoint that supports the urn:ietf:params:oauth:grant-type:uma-ticket grant type. Overrides value from discovery, if given.
resource_registration_endpointstringoptionalhttps://host.domain/auth/realms/foo/authz/protection/resource_setA Keycloak Protection API-compliant resource registration endpoint. Overrides value from discovery, if given.
client_idstringoptionalThe client identifier of the resource server to which the client is seeking access. One of client_id or audience is required.
audiencestringoptionalLegacy parameter now replaced by client_id. Kept for backwards compatibility. One of client_id or audience is required.
client_secretstringoptionalThe client secret, if required.
grant_typestringoptional"urn:ietf:params:oauth:grant-type:uma-ticket"["urn:ietf:params:oauth:grant-type:uma-ticket"]
policy_enforcement_modestringoptional"ENFORCING"["ENFORCING", "PERMISSIVE"]
permissionsarray[string]optionalStatic permission to request, an array of strings each representing a resources and optionally one or more scopes the client is seeking access.
lazy_load_pathsbooleanoptionalfalseDynamically resolve the request URI to resource(s) using the resource registration endpoint instead of using the static permission.
http_method_as_scopebooleanoptionalfalseMap HTTP request type to scope of same name and add to all permissions requested.
timeoutintegeroptional3000[1000, ...]Timeout(ms) for the http connection with the Identity Server.
ssl_verifybooleanoptionaltrueVerify if TLS certificate matches hostname.
cache_ttl_secondsintegeroptional86400 (equivalent to 24h)positive integer >= 1The maximum period in seconds up to which the plugin caches discovery documents and tokens, used by the plugin to authenticate to Keycloak.
keepalivebooleanoptionaltrueEnable HTTP keep-alive to keep connections open after use. Set to true if you expect a lot of requests to Keycloak.
keepalive_timeoutintegeroptional60000positive integer >= 1000Idle timeout after which established HTTP connections will be closed.
keepalive_poolintegeroptional5positive integer >= 1Maximum number of connections in the connection pool.

Discovery and Endpoints#

The plugin can discover Keycloak API endpoints from a URL in the discovery attribute that points to Keycloak's discovery document for Authorization Services for the respective realm. This is the recommended option and typically most convenient.

If the discovery document is available, the plugin determines the token endpoint URL from it. If present, the token_endpoint attribute overrides the URL.

Analogously, the plugin determines the registration endpoint from the discovery document. The resource_registration_endpoint overrides, if present.

Client ID and Secret#

The plugin needs the client_id attribute to identify itself when interacting with Keycloak. For backwards compatibility, you can still use the audience attribute as well instead. The plugin prefers client_id over audience if both are configured.

The plugin always needs the client_id or audience to specify the context in which Keycloak should evaluate permissions.

If lazy_load_paths is true then the plugin additionally needs to obtain an access token for itself from Keycloak. In this case, if the client access to Keycloak is confidential, the plugin needs the client_secret attribute as well.

Policy Enforcement Mode#

Specifies how policies are enforced when processing authorization requests sent to the server.

Enforcing

  • (default mode) Requests are denied by default even when there is no policy associated with a given resource.

Permissive

  • Requests are allowed even when there is no policy associated with a given resource.

Permissions#

When handling an incoming request, the plugin can determine the permissions to check with Keycloak either statically, or dynamically from properties of the request.

If lazy_load_paths is false, the plugin takes the permissions from the permissions attribute. Each entry needs to be formatted as expected by the token endpoint's permission parameter; see https://www.keycloak.org/docs/latest/authorization_services/index.html#_service_obtaining_permissions. Note that a valid permission can be a single resource, or a resource paired with one or more scopes.

if lazy_load_paths is true, the plugin resolves the request URI to one or more resources, as configured in Keycloak. It uses the resource registration endpoint to do so. The plugin uses the resolved resources as the permissions to check.

Note that this requires that the plugin can obtain a separate access token for itself from the token endpoint. Therefore, in the respective client settings in Keycloak, make sure to set the Service Accounts Enabled option. Also make sure that the issued access token contains the resource_access claim with the uma_protection role. Otherwise, plugin may be unable to query resources through the Protection API.

Automatic Mapping of HTTP Method to Scope#

This option is often used together with lazy_load_paths, but can also be used with a static permission list.

If the http_method_as_scope attribute is set to true, the plugin maps the request's HTTP method to a scope of the same name. The scope is then added to every permission to check.

If lazy_load_paths is false, the plugin adds the mapped scope to any of the static permissions configured in the permissions attribute, even if they contain one or more scopes already.

How To Enable#

Create a route and enable the authz-keycloak plugin on the route:

curl http://127.0.0.1:9080/apisix/admin/routes/5 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '{    "uri": "/get",    "plugins": {        "authz-keycloak": {            "token_endpoint": "http://127.0.0.1:8090/auth/realms/{client_id}/protocol/openid-connect/token",            "permissions": ["resource name#scope name"],            "audience": "Client ID"        }    },    "upstream": {        "type": "roundrobin",        "nodes": {            "127.0.0.1:8080": 1        }    }}'

Test Plugin#

curl http://127.0.0.1:9080/get -H 'Authorization: Bearer {JWT Token}'

Disable Plugin#

Remove the corresponding json configuration in the plugin configuration to disable the authz-keycloak. APISIX plugins are hot-reloaded, therefore no need to restart APISIX.

curl http://127.0.0.1:9080/apisix/admin/routes/5 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '{    "uri": "/get",    "plugins": {    },    "upstream": {        "type": "roundrobin",        "nodes": {            "127.0.0.1:8080": 1        }    }}'

Examples#

Checkout the unit test for of the authz-keycloak.t to understand how the authorization policies can be integrated into your API workflows. Run the following docker image and visit http://localhost:8090 to view the associated policies for the unit tests.

docker run -e KEYCLOAK_USER=admin -e KEYCLOAK_PASSWORD=123456 -p 8090:8080 sshniro/keycloak-apisix

The following image shows how the policies are configures in the Keycloak server.

Keycloak policy design

Future Development#

  • Currently the authz-plugin requires to define the resource name and required scopes in order to enforce policies for the routes. However, Keycloak's official adapters (Java, JS) also provides path matching by querying Keycloak paths dynamically, and lazy loading the paths to identity resources. Future version on authz-plugin will support this functionality.

  • Support to read scope and configurations from the Keycloak JSON File