apache / apisix

The Cloud-Native API Gateway
https://apisix.apache.org/blog/
Apache License 2.0
14.35k stars 2.5k forks source link

feat: Using authz-keycloak plugin, I want to add claims to an authz request #11094

Open Java-Superman opened 6 months ago

Java-Superman commented 6 months ago

Add claim_token to authz request

Using the JS Policy in Keycloak, I need to know the parameters in the URL to decide if the user has permission. An example would be how to handle the path /region/1234/customers, where the user must belong to group.regionId=1234

Claims can be added to the authz request like this example.

curl -X POST \
  http://${host}:${port}/realms/${realm}/protocol/openid-connect/token \
  --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \
  --data "claim_token=ewogICAib3JnYW5pemF0aW9uIjogWyJhY21lIl0KfQ==" \
  --data "claim_token_format=urn:ietf:params:oauth:token-type:jwt" \
  --data "client_id={resource_server_client_id}" \
  --data "client_secret={resource_server_client_secret}" \
  --data "audience={resource_server_client_id}"

It would be nice if the plugin added the uri (possibly other things) to the claim_token.

claim_token = base64( { "uri" : $request_uri } ) put in authz-keycloak.lua

In Java using Keycloak's ServletPolicyEnforcerFilter, an example configuration file for claims looks like this.

{
  ....
  "paths": [
    {
      "path": "/protected/resource",
      "claim-information-point": {
        "claims": {
          "claim-from-request-parameter": "{request.parameter['a']}",
          "claim-from-header": "{request.header['b']}",
          "claim-from-cookie": "{request.cookie['c']}",
          "claim-from-remoteAddr": "{request.remoteAddr}",
  ...
}

Use the request path for the permission

For authz-keycloak.lua, I believe the permission config parameter could be optional if the authz request used permission=$url and permission_resource_matching_uri=true when permission is not supplied (see here). This would allow permissions to be controlled based on the path within keycloak and not have the additional step of adding all the permissions to APISIX.

NOTE: Relates to https://github.com/apache/apisix/issues/7190

shreemaan-abhishek commented 6 months ago

how would these claims be passed to apisix? I mean how would apisix know which claims to pass?

Java-Superman commented 6 months ago

@shreemaan-abhishek

how would these claims be passed to apisix? I mean how would apisix know which claims to pass?

Defining it within the attributes of the authz-keycloak plugin. I'm not a lua dev, but something like:

claims : { "uri" : "var.uri", "myparam" : "var.arg_myparam" }

where they would be evaluated at runtime. I'll have to defer to you guys on how the properties above could be evaluated at runtime (finite set of things/eval method).

shreemaan-abhishek commented 6 months ago

Defining it within the attributes of the authz-keycloak plugin. I'm not a lua dev, but something like:

claims : { "uri" : "var.uri", "myparam" : "var.arg_myparam" }

where they would be evaluated at runtime. I'll have to defer to you guys on how the properties above could be evaluated at runtime (finite set of things/eval method).

hardcoding them doesn't make sense to me.

Java-Superman commented 6 months ago

@shreemaan-abhishek

hardcoding them doesn't make sense to me.

I agree, but I'm suggesting the attribute values within the claims object would need to be evaluated within the lua script during a request.

For the example (changing syntax to use {} to allow for static values):

"plugins": {
  "authz-keycloak": {
    "claims": {
      "uri": "{request.uri}",
      "my-param": "{request.paramater['myparam']}",
    }
  }
}

During an http request

The claim_token passed in the lua script would be:

claim_token = base64( stringify( { "uri": "/foo?myparam=1234", "my-param": "1234" } ) )

Basically, anything in the {} should be parsed and replaced with the appropriate value. Below are a few examples that would be nice to support.

Description Example Configuration String
Static Value "anything you want"
Remote Address "{request.remoteAddr}"
Method "{request.method}"
URI "{request.uri}"
URL "{request.url}"
Secure "{request.secure}"
Request Parameter "{request.parameter['a']}"
Header "{request.header['b']}"
Cookie "{request.cookie['c']}"
Replace Multiple Placeholders "called {request.method} {request.url}"
shreemaan-abhishek commented 6 months ago

ah okay, makes sense to me now. I'm a noob when it comes to oidc/keycloak stuff.

This looks like a good to have but a big feature nevertheless. Let's wait for the community to drop their feedback.