kayx23 commented on code in PR #13246:
URL: https://github.com/apache/apisix/pull/13246#discussion_r3109664849


##########
docs/en/latest/plugins/authz-keycloak.md:
##########
@@ -28,214 +28,737 @@ description: This document contains information about the 
Apache APISIX authz-ke
 #
 -->
 
-## Description
-
-The `authz-keycloak` Plugin can be used to add authentication with [Keycloak 
Identity Server](https://www.keycloak.org/).
+<head>
+  <link rel="canonical" href="https://docs.api7.ai/hub/authz-keycloak"; />
+</head>
 
-:::tip
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
 
-Although this Plugin was developed to work with Keycloak, it should work with 
any OAuth/OIDC and UMA compliant identity providers as well.
+## Description
 
-:::
+The `authz-keycloak` Plugin supports the integration with 
[Keycloak](https://www.keycloak.org/) to authenticate and authorize users. See 
Keycloak's [Authorization Services 
Guide](https://www.keycloak.org/docs/latest/authorization_services/) for more 
information about the configuration options available in this Plugin.
 
-Refer to [Authorization Services 
Guide](https://www.keycloak.org/docs/latest/authorization_services/) for more 
information on Keycloak.
+While the Plugin was developed for Keycloak, it could theoretically be used 
with other OAuth/OIDC and UMA-compliant identity providers.
 
 ## Attributes
 
-| Name                                         | Type          | Required | 
Default                                       | Valid values                    
                                   | Description                                
                                                                                
                                                                                
                                           |
-|----------------------------------------------|---------------|----------|-----------------------------------------------|--------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| discovery                                    | string        | False    |    
                                           | 
https://host.domain/realms/foo/.well-known/uma2-configuration | URL to 
[discovery 
document](https://www.keycloak.org/docs/latest/authorization_services/index.html)
 of Keycloak Authorization Services.                                            
                                                    |
-| token_endpoint                               | string        | False    |    
                                           | 
https://host.domain/realms/foo/protocol/openid-connect/token  | An 
OAuth2-compliant token endpoint that supports the 
`urn:ietf:params:oauth:grant-type:uma-ticket` grant type. If provided, 
overrides the value from discovery.                                             
                                          |
-| resource_registration_endpoint               | string        | False    |    
                                           | 
https://host.domain/realms/foo/authz/protection/resource_set  | A UMA-compliant 
resource registration endpoint. If provided, overrides the value from 
discovery.                                                                      
                                                                                
|
-| client_id                                    | string        | True     |    
                                           |                                    
                                | The identifier of the resource server to 
which the client is seeking access.                                             
                                                                                
                                            |
-| client_secret                                | string        | False    |    
                                           |                                    
                                | The client secret, if required. You can use 
APISIX secret to store and reference this value. APISIX currently supports 
storing secrets in two ways. [Environment Variables and HashiCorp 
Vault](../terminology/secret.md)                                                
                                                                                
                                                                                
         |
-| grant_type                                   | string        | False    | 
"urn:ietf:params:oauth:grant-type:uma-ticket" | 
["urn:ietf:params:oauth:grant-type:uma-ticket"]                    |            
                                                                                
                                                                                
                                                                           |
-| policy_enforcement_mode                      | string        | False    | 
"ENFORCING"                                   | ["ENFORCING", "PERMISSIVE"]     
                                   |                                            
                                                                                
                                                                                
                                           |
-| permissions                                  | array[string] | False    |    
                                           |                                    
                                | An array of strings, each representing a set 
of one or more resources and scopes the client is seeking access.               
                                                                                
                                         |
-| lazy_load_paths                              | boolean       | False    | 
false                                         |                                 
                                   | When set to true, dynamically resolves the 
request URI to resource(s) using the resource registration endpoint instead of 
the static permission.                                                          
                                            |
-| http_method_as_scope                         | boolean       | False    | 
false                                         |                                 
                                   | When set to true, maps the HTTP request 
type to scope of the same name and adds to all requested permissions.           
                                                                                
                                              |
-| timeout                                      | integer       | False    | 
3000                                          | [1000, ...]                     
                                   | Timeout in ms for the HTTP connection with 
the Identity Server.                                                            
                                                                                
                                           |
-| access_token_expires_in                      | integer       | False    | 
300                                           | [1, ...]                        
                                   | Expiration time(s) of the access token.    
                                                                                
                                                                                
                                           |
-| access_token_expires_leeway                  | integer       | False    | 0  
                                           | [0, ...]                           
                                | Expiration leeway(s) for access_token 
renewal. When set, the token will be renewed access_token_expires_leeway 
seconds before expiration. This avoids errors in cases where the access_token 
just expires when reaching the OAuth Resource Server.    |
-| refresh_token_expires_in                     | integer       | False    | 
3600                                          | [1, ...]                        
                                   | The expiration time(s) of the refresh 
token.                                                                          
                                                                                
                                                |
-| refresh_token_expires_leeway                 | integer       | False    | 0  
                                           | [0, ...]                           
                                | Expiration leeway(s) for refresh_token 
renewal. When set, the token will be renewed refresh_token_expires_leeway 
seconds before expiration. This avoids errors in cases where the refresh_token 
just expires when reaching the OAuth Resource Server. |
-| ssl_verify                                   | boolean       | False    | 
true                                          |                                 
                                   | When set to true, verifies if TLS 
certificate matches hostname.                                                   
                                                                                
                                                    |
-| cache_ttl_seconds                            | integer       | False    | 
86400 (equivalent to 24h)                     | positive integer >= 1           
                                   | Maximum time in seconds up to which the 
Plugin caches discovery documents and tokens used by the Plugin to authenticate 
to Keycloak.                                                                    
                                              |
-| keepalive                                    | boolean       | False    | 
true                                          |                                 
                                   | When set to true, enables HTTP keep-alive 
to keep connections open after use. Set to `true` if you are expecting a lot of 
requests to Keycloak.                                                           
                                            |
-| keepalive_timeout                            | integer       | False    | 
60000                                         | positive integer >= 1000        
                                   | Idle time after which the established HTTP 
connections will be closed.                                                     
                                                                                
                                           |
-| keepalive_pool                               | integer       | False    | 5  
                                           | positive integer >= 1              
                                | Maximum number of connections in the 
connection pool.                                                                
                                                                                
                                                 |
-| access_denied_redirect_uri                   | string        | False    |    
                                           | [1, 2048]                          
                                | URI to redirect the user to instead of 
returning an error message like `"error_description":"not_authorized"`.         
                                                                                
                                               |
-| password_grant_token_generation_incoming_uri | string        | False    |    
                                           | /api/token                         
                                | Set this to generate token using the password 
grant type. The Plugin will compare incoming request URI to this value.         
                                                                                
                                        |
+| Name | Type | Required | Default | Valid values | Description |
+|------|------|----------|---------|--------------|-------------|
+| client_id | string | True | | | Client ID. |
+| client_secret | string | False | | | Client secret. The value is encrypted 
with AES before being stored in etcd. |
+| discovery | string | False | | | URL to the discovery document. |
+| token_endpoint | string | False | | | Token endpoint that supports the 
`urn:ietf:params:oauth:grant-type:uma-ticket` grant type to obtain access 
token. If provided, overrides the value from the discovery document. |
+| resource_registration_endpoint | string | False | | | A UMA-compliant 
resource registration endpoint. Required when `lazy_load_paths` is `true`. The 
Plugin will first look for the resource registration endpoint from this 
configuration option; if not found, look for the resource registration endpoint 
from the discovery document. |
+| grant_type | string | False | `urn:ietf:params:oauth:grant-type:uma-ticket` 
| `urn:ietf:params:oauth:grant-type:uma-ticket` | Must be set to 
`urn:ietf:params:oauth:grant-type:uma-ticket`. |
+| policy_enforcement_mode | string | False | `ENFORCING` | `ENFORCING` or 
`PERMISSIVE` | The mode of [policy 
enforcement](https://www.keycloak.org/docs/latest/authorization_services/index.html#policy-enforcement).
 In `ENFORCING` mode, requests are denied when there is no policy associated 
with a given resource. In `PERMISSIVE` mode, requests are allowed when there is 
no policy associated with a given resource. |
+| permissions | array[string] | False | | | An array of permissions 
representing a set of resources and scopes the client is seeking access. The 
format could be `RESOURCE_ID#SCOPE_ID`, `RESOURCE_ID`, or `#SCOPE_ID`. Used 
when `lazy_load_paths` is `false`. See [obtaining 
permissions](https://www.keycloak.org/docs/latest/authorization_services/index.html#_service_obtaining_permissions).
 |
+| lazy_load_paths | boolean | False | `false` | | If `true`, require discovery 
or resource registration endpoint to dynamically resolve the request URI to 
resources. This requires the Plugin to obtain a separate access token for 
itself from the token endpoint. Make sure the `Service Accounts Enabled` option 
is checked in Keycloak to allow for client credentials grant, and that the 
issued access token contains the `resource_access` claim with the 
`uma_protection` role for the Plugin to query resources through the [Protection 
API](https://www.keycloak.org/docs/latest/authorization_services/index.html#authorization-services).
 |
+| http_method_as_scope | boolean | False | `false` | | If `true`, use the HTTP 
method of the request as the scope to check whether access should be granted. 
When `lazy_load_paths` is `false`, the Plugin adds the mapped scope to any of 
the static permissions configured in the `permissions` attribute, even when 
they contain one or more scopes already. |
+| timeout | integer | False | 3000 | >= 1000 | Timeout in milliseconds for the 
HTTP connection with the identity provider. |
+| access_token_expires_in | integer | False | 300 | >= 1 | Lifetime of the 
access token in seconds if no `expires_in` attribute is present in the token 
endpoint response. |
+| access_token_expires_leeway | integer | False | 0 | >= 0 | Expiration leeway 
in seconds for access token renewal. When set to a value greater than 0, token 
renewal will take place the configured amount of time before token expiration. |
+| refresh_token_expires_in | integer | False | 3600 | > 0 | Expiration time of 
the refresh token in seconds. |
+| refresh_token_expires_leeway | integer | False | 0 | >= 0 | Expiration 
leeway in seconds for refresh token renewal. When set to a value greater than 
0, token renewal will take place the configured amount of time before token 
expiration. |
+| ssl_verify | boolean | False | `true` | | If `true`, verify the OpenID 
provider's SSL certificates. |
+| cache_ttl_seconds | integer | False | 86400 | > 0 | TTL in seconds for the 
Plugin to cache discovery document and access tokens. |
+| keepalive | boolean | False | `true` | | If `true`, enable HTTP keep-alive 
to keep connections open after use. Set to `true` if you are expecting a lot of 
requests to Keycloak. |
+| keepalive_timeout | integer | False | 60000 | >= 1000 | Idle time after 
which the established HTTP connections will be closed. |
+| keepalive_pool | integer | False | 5 | >= 1 | Maximum number of connections 
in the connection pool. |
+| access_denied_redirect_uri | string | False | | | URI to redirect the user 
to instead of returning an error message like 
`"error_description":"not_authorized"` when access is denied. |
+| password_grant_token_generation_incoming_uri | string | False | | | The URI 
incoming requests hit to generate a token using the password grant, for 
example, `/api/token`. If the incoming request's URI matches the configured 
value, the request method is POST, and `Content-Type` is 
`application/x-www-form-urlencoded`, a token is generated at the 
`token_endpoint`. |
 
 NOTE: `encrypt_fields = {"client_secret"}` is also defined in the schema, 
which means that the field will be stored encrypted in etcd. See [encrypted 
storage fields](../plugin-develop.md#encrypted-storage-fields).
 
-### Discovery and endpoints
+## Examples
 
-It is recommended to use the `discovery` attribute as the `authz-keycloak` 
Plugin can discover the Keycloak API endpoints from it.
+The examples below demonstrate how you can configure `authz-keycloak` for 
different scenarios.
 
-If set, the `token_endpoint` and `resource_registration_endpoint` will 
override the values obtained from the discovery document.
+To follow along, complete the [preliminary setups](#set-up-keycloak) for 
Keycloak.
 
-### Client ID and secret
+:::note
 
-The Plugin needs the `client_id` attribute for identification and to specify 
the context in which to evaluate permissions when interacting with Keycloak.
+You can fetch the `admin_key` from `conf/config.yaml` and save to an 
environment variable with the following command:
 
-If the `lazy_load_paths` attribute is set to true, then the Plugin 
additionally needs to obtain an access token for itself from Keycloak. In such 
cases, if the client access to Keycloak is confidential, you need to configure 
the `client_secret` attribute.
+```shell
+admin_key=$(yq '.deployment.admin.admin_key[0].key' /conf/config.yaml | sed 
's/"//g')
+```
 
-### Policy enforcement mode
+:::
 
-The `policy_enforcement_mode` attribute specifies how policies are enforced 
when processing authorization requests sent to the server.
+### Set Up Keycloak
 
-#### `ENFORCING` mode
+#### Start Keycloak
 
-Requests are denied by default even when there is no policy associated with a 
resource.
+Start a Keycloak instance named `apisix-quickstart-keycloak` with the 
administrator name `quickstart-admin` and password `quickstart-admin-pass` in 
[development 
mode](https://www.keycloak.org/server/configuration#_starting_keycloak_in_development_mode)
 in Docker:
 
-The `policy_enforcement_mode` is set to `ENFORCING` by default.
+```shell
+docker run -d --name "apisix-quickstart-keycloak" \
+  -e 'KEYCLOAK_ADMIN=quickstart-admin' \
+  -e 'KEYCLOAK_ADMIN_PASSWORD=quickstart-admin-pass' \
+  -p 8080:8080 \
+  quay.io/keycloak/keycloak:18.0.2 start-dev
+```
 
-#### `PERMISSIVE` mode
+Save the Keycloak IP to an environment variable:
 
-Requests are allowed when there is no policy associated with a given resource.
+```shell
+KEYCLOAK_IP=192.168.42.145    # replace with your host IP
+```
 
-### Permissions
+Navigate to `http://localhost:8080` in a browser and click **Administration 
Console**. Enter the administrator username `quickstart-admin` and password 
`quickstart-admin-pass` to sign in.
 
-When handling incoming requests, the Plugin can determine the permissions to 
check with Keycloak statically or dynamically from the properties of the 
request.
+#### Create a Realm
 
-If the `lazy_load_paths` attribute is set to `false`, the permissions are 
taken from the `permissions` attribute. Each entry in `permissions` needs to be 
formatted as expected by the token endpoint's `permission` parameter. See 
[Obtaining 
Permissions](https://www.keycloak.org/docs/latest/authorization_services/index.html#_service_obtaining_permissions).
+In the left menu, hover over **Master**, and select **Add realm** in the 
dropdown. Enter the realm name `quickstart-realm` and click **Create**.
 
-:::note
+#### Create a Client
 
-A valid permission can be a single resource or a resource paired with on or 
more scopes.
+Click **Clients** > **Create** to open the **Add Client** page. Enter **Client 
ID** as `apisix-quickstart-client`, keep the **Client Protocol** as 
`openid-connect`, and click **Save**.
 
-:::
+After redirecting to the detailed page, select `confidential` as the **Access 
Type**. Enter wildcard `*` in **Valid Redirect URIs** for simplicity.
 
-If the `lazy_load_paths` attribute is set to `true`, the request URI is 
resolved to one or more resources configured in Keycloak using the resource 
registration endpoint. The resolved resources are used as the permissions to 
check.
+Enable authorization for the Client, which also enables service accounts with 
the `uma_protection` role automatically. Click **Save**.
 
-:::note
+#### Save Client ID and Secret
 
-This requires the Plugin to obtain a separate access token for itself from the 
token endpoint. So, make sure to set the `Service Accounts Enabled` option in 
the client settings in Keycloak.
+Click **Clients** > `apisix-quickstart-client` > **Credentials**, and copy the 
Client secret from **Secret**.
 
-Also make sure that the issued access token contains the `resource_access` 
claim with the `uma_protection` role to ensure that the Plugin is able to query 
resources through the Protection API.
+Save the OIDC Client ID and secret to environment variables:
 
-:::
+```shell
+OIDC_CLIENT_ID=apisix-quickstart-client
+OIDC_CLIENT_SECRET=bSaIN3MV1YynmtXvU8lKkfeY0iwpr9cH  # replace with your value
+```
 
-### Automatically mapping HTTP method to scope
+#### Request Access Token
 
-The `http_method_as_scope` is often used together with `lazy_load_paths` but 
can also be used with a static permission list.
+Request an access token from Keycloak:
 
-If the `http_method_as_scope` attribute is set to `true`, the Plugin maps the 
request's HTTP method to the scope with the same name. The scope is then added 
to every permission to check.
+```shell
+curl -i 
"http://$KEYCLOAK_IP:8080/realms/quickstart-realm/protocol/openid-connect/token";
 -X POST \
+  -d 'grant_type=client_credentials' \
+  -d 'client_id='$OIDC_CLIENT_ID'' \
+  -d 'client_secret='$OIDC_CLIENT_SECRET''
+```
+
+Save the access token to an environment variable:
 
-If the `lazy_load_paths` attribute is set to false, the Plugin adds the mapped 
scope to any of the static permissions configured in the `permissions` 
attribute—even if they contain on or more scopes already.
+```shell
+ACCESS_TOKEN=<your_access_token>  # replace with the access_token value from 
the response
+```
 
-### Generating a token using `password` grant
+### Use Lazy Load Path and Resource Registration Endpoint
 
-To generate a token using `password` grant, you can set the value of the 
`password_grant_token_generation_incoming_uri` attribute.
+The following example demonstrates how you can configure the Plugin to 
dynamically resolve the request URI to resource(s) using the resource 
registration endpoint instead of static permissions.
 
-If the incoming URI matches the configured attribute and the request method is 
POST, a token is generated using the `token_endpoint`.
+Create a Route with the `authz-keycloak` Plugin:
 
-You also need to add `application/x-www-form-urlencoded` as `Content-Type` 
header and `username` and `password` as parameters.
+<Tabs
+groupId="api"
+defaultValue="admin-api"
+values={[
+{label: 'Admin API', value: 'admin-api'},
+{label: 'ADC', value: 'adc'},
+{label: 'Ingress Controller', value: 'aic'}
+]}>
 
-The example below shows a request if the 
`password_grant_token_generation_incoming_uri` is `/api/token`:
+<TabItem value="admin-api">
 
 ```shell
-curl --location --request POST 'http://127.0.0.1:9080/api/token' \
---header 'Accept: application/json, text/plain, */*' \
---header 'Content-Type: application/x-www-form-urlencoded' \
---data-urlencode 'username=<User_Name>' \
---data-urlencode 'password=<Password>'
+curl "http://127.0.0.1:9180/apisix/admin/routes"; -X PUT \
+  -H "X-API-KEY: ${admin_key}" \
+  -d '{
+    "id": "authz-keycloak-route",
+    "uri": "/anything",
+    "plugins": {
+      "authz-keycloak": {
+        "lazy_load_paths": true,
+        "resource_registration_endpoint": 
"http://'"$KEYCLOAK_IP"':8080/realms/quickstart-realm/authz/protection/resource_set",
+        "discovery": 
"http://'"$KEYCLOAK_IP"':8080/realms/quickstart-realm/.well-known/uma2-configuration",
+        "client_id": "'"$OIDC_CLIENT_ID"'",
+        "client_secret": "'"$OIDC_CLIENT_SECRET"'"
+      }
+    },
+    "upstream": {
+      "type": "roundrobin",
+      "nodes": {
+        "httpbin.org": 1
+      }
+    }
+  }'
 ```
 
-## Enable Plugin
+</TabItem>
+
+<TabItem value="adc">
+
+```yaml title="adc.yaml"
+services:
+  - name: authz-keycloak-service
+    routes:
+      - name: authz-keycloak-route
+        uris:
+          - /anything
+        plugins:
+          authz-keycloak:
+            lazy_load_paths: true
+            resource_registration_endpoint: 
"http://<KEYCLOAK_IP>:8080/realms/quickstart-realm/authz/protection/resource_set"
+            discovery: 
"http://<KEYCLOAK_IP>:8080/realms/quickstart-realm/.well-known/uma2-configuration"
+            client_id: "apisix-quickstart-client"
+            client_secret: "<OIDC_CLIENT_SECRET>"
+    upstream:
+      type: roundrobin
+      nodes:
+        - host: httpbin.org
+          port: 80
+          weight: 1
+```
 
-The example below shows how you can enable the `authz-keycloak` Plugin on a 
specific Route. `${realm}` represents the realm name in Keycloak.
+Synchronize the configuration to the gateway:
 
-:::note
-You can fetch the `admin_key` from `config.yaml` and save to an environment 
variable with the following command:
+```shell
+adc sync -f adc.yaml
+```
 
-```bash
-admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 
's/"//g')
+</TabItem>
+
+<TabItem value="aic">
+
+<Tabs
+groupId="k8s-api"
+defaultValue="gateway-api"
+values={[
+{label: 'Gateway API', value: 'gateway-api'},
+{label: 'APISIX Ingress Controller', value: 'apisix-ingress-controller'}
+]}>
+
+<TabItem value="gateway-api">
+
+```yaml title="authz-keycloak-ic.yaml"
+apiVersion: apisix.apache.org/v1alpha1
+kind: PluginConfig
+metadata:
+  namespace: aic
+  name: authz-keycloak-plugin-config
+spec:
+  plugins:
+    - name: authz-keycloak
+      config:
+        lazy_load_paths: true
+        resource_registration_endpoint: 
"http://<KEYCLOAK_IP>:8080/realms/quickstart-realm/authz/protection/resource_set"
+        discovery: 
"http://<KEYCLOAK_IP>:8080/realms/quickstart-realm/.well-known/uma2-configuration"
+        client_id: "apisix-quickstart-client"
+        client_secret: "<OIDC_CLIENT_SECRET>"
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+  namespace: aic
+  name: authz-keycloak-route
+spec:
+  parentRefs:
+    - name: apisix
+  rules:
+    - matches:
+        - path:
+            type: Exact
+            value: /anything
+      filters:
+        - type: ExtensionRef
+          extensionRef:
+            group: apisix.apache.org
+            kind: PluginConfig
+            name: authz-keycloak-plugin-config
+      backendRefs:
+        - name: httpbin-external-domain

Review Comment:
   This Gateway API example references `httpbin-external-domain`, but the 
manifest in this section never creates that `Service`. As written, the route 
points to a backend that does not exist, so readers who copy this example will 
not get a working setup. Could we add the missing `Service` resource here (and 
in the other Gateway API sections in this PR that reuse the same backend)?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to