Docs
Plugin HubOverview

acl

The acl plugin authorizes requests by matching authenticated consumer or external-user labels against allow or deny lists. It supports custom rejection responses and extracts labels from flat or nested data.

Consumer-label policies work in APISIX and API7 Gateway. Compatibility with external users depends on the authentication plugin. The Keycloak OIDC examples on this page are specific to API7 Gateway because the APISIX 3.18 openid-connect plugin does not make OIDC user information available to acl.

Examples

The examples show how to authorize consumers by their labels and how API7 Gateway can authorize users by group information returned from an identity provider.

Control Access by Consumer Labels

The following example uses key-auth to authenticate two consumers. The acl plugin allows only the consumer whose org label contains opensource.

Create consumer john with the required organization label:

curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \
  -H "X-API-KEY: ${ADMIN_API_KEY}" \
  -d '{
    "username": "john",
    "labels": {
      "org": "[\"opensource\",\"apache\"]",
      "project": "[\"tomcat\",\"web-server\",\"http,server\"]"
    }
  }'

Create consumer jane with different labels:

curl "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT \
  -H "X-API-KEY: ${ADMIN_API_KEY}" \
  -d '{
    "username": "jane",
    "labels": {
      "org": "apache",
      "project": "gateway,apisix,web-server"
    }
  }'

Create a key-auth credential for john:

curl "http://127.0.0.1:9180/apisix/admin/consumers/john/credentials" -X PUT \
  -H "X-API-KEY: ${ADMIN_API_KEY}" \
  -d '{
    "id": "cred-john-key-auth",
    "plugins": {
      "key-auth": {
        "key": "john-key"
      }
    }
  }'

Create a separate credential for jane:

curl "http://127.0.0.1:9180/apisix/admin/consumers/jane/credentials" -X PUT \
  -H "X-API-KEY: ${ADMIN_API_KEY}" \
  -d '{
    "id": "cred-jane-key-auth",
    "plugins": {
      "key-auth": {
        "key": "jane-key"
      }
    }
  }'

Consumer label values can be scalars, comma-separated strings, or JSON arrays encoded as strings. Create a route that allows consumers whose org label contains opensource:

curl "http://127.0.0.1:9180/apisix/admin/routes/acl-consumer-labels" -X PUT \
  -H "X-API-KEY: ${ADMIN_API_KEY}" \
  -d '{
    "uri": "/get",
    "plugins": {
      "key-auth": {},
      "acl": {
        "allow_labels": {
          "org": ["opensource"]
        }
      }
    },
    "upstream": {
      "type": "roundrobin",
      "nodes": {
        "httpbin.org:80": 1
      }
    }
  }'

❶ Allows a request when the authenticated consumer has an org label whose value matches opensource.

Send a request as consumer jane:

curl -i "http://127.0.0.1:9080/get" -H "apikey: jane-key"

The response should be HTTP/1.1 403 Forbidden because jane does not have the required label.

Send the same request as consumer john:

curl -i "http://127.0.0.1:9080/get" -H "apikey: john-key"

The response should be HTTP/1.1 200 OK because john has the required label.

Control Access by OIDC User Groups

This example uses Keycloak groups returned in OIDC user information to authorize browser requests.

info

The OIDC user-information examples are specific to API7 Gateway. Its openid-connect plugin makes authenticated user information available to acl. APISIX 3.18 does not provide this integration between the two plugins.

Configure Keycloak Groups

Complete the Keycloak realm, confidential OIDC client, and user setup in Set Up SSO with Keycloak. Keep Authorization Code and S256 PKCE enabled, and register http://localhost:9080/anything/user/callback as a valid redirect URI.

Create two groups and assign them to the test user:

  1. Select Groups → Create group, create apisix, and repeat the action for opensource.
  2. Select Users → quickstart-user → Groups → Join Group.
  3. Select apisix and opensource, then select Join.

Keycloak Groups page showing two configured groups

Add the groups to OIDC user information:

  1. Select Clients → apisix-quickstart-client → Client scopes.

  2. Open apisix-quickstart-client-dedicated, select Add mapper → By configuration, and select Group Membership.

  3. Configure the following fields:

    FieldValue
    Namegroups
    Token Claim Namegroups
    Full group pathOn
    Add to userinfoOn
  4. Select Save.

Keycloak Group Membership mapper configured with the groups user information claim

Save the Keycloak client details as environment variables, replacing the example values:

export KEYCLOAK_HOST=replace-with-keycloak-host
export KEYCLOAK_DISCOVERY="http://${KEYCLOAK_HOST}:8080/realms/quickstart-realm/.well-known/openid-configuration"
export KEYCLOAK_CLIENT_ID=apisix-quickstart-client
export KEYCLOAK_CLIENT_SECRET=replace-with-client-secret
export API7_SESSION_SECRET="$(openssl rand -hex 32)"

The gateway container and the browser must be able to reach Keycloak at the same host address. Use HTTPS and store the client and session secrets in a secret-management system for production deployments.

Configure API7 Gateway

Create a protected route that starts browser authentication and allows users in the /apisix Keycloak group.

Create the route through the Admin API:

curl "http://127.0.0.1:9180/apisix/admin/routes/acl-oidc-groups" -X PUT \
  -H "X-API-KEY: ${ADMIN_API_KEY}" \
  -d @- <<EOF
{
  "uri": "/anything/user/*",
  "plugins": {
    "openid-connect": {
      "client_id": "$KEYCLOAK_CLIENT_ID",
      "client_secret": "$KEYCLOAK_CLIENT_SECRET",
      "discovery": "$KEYCLOAK_DISCOVERY",
      "redirect_uri": "http://localhost:9080/anything/user/callback",
      "bearer_only": false,
      "use_pkce": true,
      "scope": "openid profile email",
      "session": {
        "secret": "$API7_SESSION_SECRET"
      },
      "set_access_token_header": false,
      "set_id_token_header": false,
      "set_userinfo_header": false
    },
    "acl": {
      "external_user_label_field": "groups",
      "allow_labels": {
        "groups": ["/apisix"]
      }
    },
    "proxy-rewrite": {
      "headers": {
        "remove": ["Authorization", "Cookie"]
      }
    }
  },
  "upstream": {
    "type": "roundrobin",
    "nodes": {
      "httpbin.org:80": 1
    }
  }
}
EOF

❶ Starts browser authentication and sends an S256 PKCE challenge to Keycloak.

❷ Reads the groups list from the OIDC user information returned by Keycloak.

❸ Allows users whose groups list contains /apisix.

❹ Removes the original authorization header and the entire cookie header, including the gateway session cookie, before proxying the request. Review this setting if the upstream application requires cookies.

Verify Group-Based Access

Navigate to http://localhost:9080/anything/user/get in a browser. API7 Gateway redirects you to Keycloak. Sign in as quickstart-user.

After authentication, API7 Gateway reads the /apisix value from the Keycloak user information, allows the request, and forwards it to the upstream. The response should contain fields similar to the following:

{
  "args": {},
  "headers": {
    "Host": "localhost",
    "X-Forwarded-Host": "localhost:9080"
  },
  "method": "GET",
  "url": "http://localhost:9080/anything/user/get"
}

The upstream response should not contain the gateway session cookie, access token, ID token, or user information headers.

To verify rejection, change /apisix under allow_labels.groups to /nonmember in the configuration created above.

Update the route through the Admin API:

curl "http://127.0.0.1:9180/apisix/admin/routes/acl-oidc-groups" -X PATCH \
  -H "X-API-KEY: ${ADMIN_API_KEY}" \
  -d '{
    "plugins": {
      "acl": {
        "allow_labels": {
          "groups": ["/nonmember"]
        }
      }
    }
  }'

Reload the protected route. The response should be HTTP/1.1 403 Forbidden because the authenticated user does not belong to /nonmember.

Control Access by Nested OIDC User Groups

API7 Gateway can also extract labels from nested OIDC user information with a JSONPath expression. This example extends the previous Keycloak configuration by returning the group list at acl_labels.nested.groups.

In the dedicated client scope, create another Group Membership mapper with the following fields:

FieldValue
Namenested-groups
Token Claim Nameacl_labels.nested.groups
Full group pathOn
Add to userinfoOn

Select Save after configuring the mapper.

Keycloak Group Membership mapper configured with a nested user information claim

Keycloak now returns the group list in a nested object similar to the following:

{
  "acl_labels": {
    "nested": {
      "groups": [
        "/apisix",
        "/opensource"
      ]
    }
  }
}

Update the acl configuration on the existing route to read the nested list.

Update the route through the Admin API:

curl "http://127.0.0.1:9180/apisix/admin/routes/acl-oidc-groups" -X PATCH \
  -H "X-API-KEY: ${ADMIN_API_KEY}" \
  -d '{
    "plugins": {
      "acl": {
        "external_user_label_field": "$.acl_labels.nested.groups",
        "external_user_label_field_key": "groups",
        "external_user_label_field_parser": "table",
        "allow_labels": {
          "groups": ["/apisix"]
        }
      }
    }
  }'

❶ Selects the nested group list with a JSONPath expression.

❷ Uses groups as the key when matching the extracted values against the access control list.

❸ Parses the Keycloak claim as a list. Use the json parser only when the selected value is a serialized JSON string.

❹ Allows users whose extracted group list contains /apisix.

Open http://localhost:9080/anything/user/get in a new private browser session and sign in again. A new authentication is required because the gateway session created in the previous example contains the earlier user information. The response should be HTTP/1.1 200 OK, confirming that API7 Gateway extracted and matched the nested Keycloak group list.