Skip to content
agentgateway has joined the Agentic AI FoundationLearn more

For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.

Page as Markdown

Authorization

Control which requests are allowed to reach your backends using authorization policies with Allow, Require, and Deny actions.

Authorization policies in agentgateway let you control which requests are allowed to reach your backends. Policies apply across all traffic types — HTTP routes, LLM providers, MCP servers, and agents — giving you a unified way to enforce access rules.

For a general overview of how policies are structured, see Policy sections.

How authorization works

Agentgateway uses the authorization field inside an AgentgatewayPolicy to evaluate whether an incoming request should be allowed or rejected. Authorization rules are expressed as Common Expression Language (CEL) expressions, which let you match on request headers, JWT claims, source IP addresses, MCP tool names, and more.

The authorization field can appear in three places in a Kubernetes policy:

Policy sectionField pathUse case
trafficspec.traffic.authorizationControl access to HTTP routes, LLM backends, or general traffic.
frontendspec.frontend.networkAuthorizationLayer 4 network-level authorization on downstream connections (such as source IP filtering).
backend.mcpspec.backend.mcp.authorizationControl access to specific MCP servers or tools.

Note

In standalone deployment mode, the frontend network authorization path is frontendPolicies.networkAuthorization.

Each authorization block contains a single action and a policy with matchExpressions. Because an authorization block takes only one action, a configuration that needs more than one action must be split across multiple AgentgatewayPolicy resources. For an example, see Combine Allow with Require.

Authorization actions

ActionBehavior
AllowGrants access when at least one expression in the policy matches, so multiple expressions are OR’d together. If any Allow rule is configured, requests that match none of them are denied. This is the recommended action for most use cases.
RequireGrants access only when every expression evaluates to true, so multiple expressions are AND’d together. Use Require to add mandatory conditions that must hold no matter which Allow rules match. See the Require example.
DenyDenies access when at least one expression matches, and overrides a matching Allow. See the warning and example below.

Evaluation order

When AgentgatewayPolicy resources are applied to a Gateway, their authorization rules are combined and evaluated as follows:

  1. Deny rules are evaluated first. If any Deny expression matches, the request is denied, even if an Allow rule also matches.
  2. Require rules are evaluated next. If any Require expression is false, the request is denied.
  3. Allow rules are evaluated last. If at least one Allow rule is configured, then at least one Allow expression must match, or the request is denied.

Note

Step 3 applies only when at least one Allow rule exists. A policy that contains only Require rules allows any request that satisfies all of those rules, because there is no allowlist to check against.

This makes Allow and Require the recommended combination for safe, readable policies.

Note that authorization runs only after authentication succeeds. A request with a missing, malformed, or unverifiable JWT fails JWT authentication and returns a 401 before any authorization expression is evaluated. Authorization denials return a 403.

Setup and test authorization

This section walks you through an end-to-end authorization setup that allows requests from one user and denies requests from another.

Before you begin

  1. Follow the Get started guide to install agentgateway.

  2. Follow the Sample app guide to create a gateway proxy with an HTTP listener and deploy the httpbin sample app.

  3. Get the external address of the gateway and save it in an environment variable.

    Tip

    Kind cluster? Kind does not support LoadBalancer services by default. To use this option with a Kind cluster, install and run cloud-provider-kind.

    export INGRESS_GW_ADDRESS=$(kubectl get svc -n agentgateway-system agentgateway-proxy -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}")
    echo $INGRESS_GW_ADDRESS  

1. Apply the authorization policy

Apply an AgentgatewayPolicy that validates JWTs and allows only requests from the user alice.

kubectl apply -f- <<EOF
apiVersion: agentgateway.dev/v1alpha1
kind: AgentgatewayPolicy
metadata:
  name: authz-guide
  namespace: agentgateway-system
spec:
  targetRefs:
  - group: gateway.networking.k8s.io
    kind: Gateway
    name: agentgateway-proxy
  traffic:
    jwtAuthentication:
      mode: Strict
      providers:
        - issuer: solo.io
          jwks:
            inline: '{"keys":[{"use":"sig","kty":"RSA","kid":"5891645032159894383","n":"5Zb1l_vtAp7DhKPNbY5qLzHIxDEIm3lpFYhBTiZyGBcnre8Y8RtNAnHpVPKdWohqhbihbVdb6U7m1E0VhLq7CS7k2Ng1LcQtVN3ekaNyk09NHuhl9LCgqXT4pATt6fYTKtZ__tEw4XKt3QqVcw7hV0YaNVC5xXGYVBh5_2-K5aW9u2LQ7FSax0jPhWdoUB3KbOQfWNOA3RwOqYn4gmc9wVToVLv6bXCVhIYWKnAVcX89C00eM7uBHENvOydD14-ZnLb4pzz2VGbU6U65odpw_i4r_mWXvoUgwogXAXp80TsYwMzLHcFo4GVDNkaH0hjuLJCeISPfYtbUJK6fFaZGBw","e":"AQAB"}]}'
    authorization:
      action: Allow
      policy:
        matchExpressions:
          - "jwt.sub == 'alice'"
EOF
Review the following table to understand this configuration.
FieldDescription
jwtAuthenticationValidates the JWT on the incoming request. Authorization expressions that reference jwt require this section, because the jwt context exists only after a token is verified. In this example, a local JWKS is provided inline. For more options, see JWT auth.
authorization.actionThe authorization action to take. Use Allow for allowlisting.
authorization.policy.matchExpressionsA list of CEL expressions. For an Allow action, at least one expression must match (OR logic). For a Require action, all expressions must evaluate to true (AND logic).

For a full list of available CEL variables you can use in expressions, see the CEL reference.

2. Save the JWT tokens

Save JWT tokens for the users Alice and Bob. Both tokens are signed by the key in the JWKS that you applied in the previous step, so both pass authentication. Only Alice’s token satisfies the authorization rule.

You can optionally create other JWT tokens by using the JWT generator tool. Note that to use JWTs with agentgateway proxies, make sure that the JWTs return Key ID (kid) and expiration date (exp) values in the JWT header.

  1. Save the JWT token for Alice, whose sub claim is alice.

    export ALICE_JWT="eyJhbGciOiJSUzI1NiIsImtpZCI6IjU4OTE2NDUwMzIxNTk4OTQzODMiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJzb2xvLmlvIiwic3ViIjoiYWxpY2UiLCJleHAiOjIwNzM2NzA0ODIsIm5iZiI6MTc2NjA4NjQ4MiwiaWF0IjoxNzY2MDg2NDgyfQ.C-KYZsfWwlwRw4cKHXWmjN5bwWD80P0CVYP6-mT5sX6BH3AR1xNrOApPF9X0plwVD4_AsWzVo435j1AmgBzPwIjhHPKtxXycaKEwSEHYFesyi-XCEJtaQZZVcjOJOs-12L2ZJeM_csk9EqKKSx0oj3jj6BciqBnLn6_hK9sEtoGenEVWEdOpkjRQBxk1m-rVZNY2IvxXMuj9C7jGXv_Sn3cU5w6arXWUsdoQtYTl5tmuF15nkD3DnQfLjDyz59FTKXUR_QkhXV81amejrDSTroJ42_RLC9ABXqdMORCe-Hus-f1utLURfAYGvmnEVeYJO8BFhedTR6lFLnVS0u2Fpw"
  2. Save the JWT token for Bob, whose sub claim is bob.

    export BOB_JWT="eyJhbGciOiJSUzI1NiIsImtpZCI6IjU4OTE2NDUwMzIxNTk4OTQzODMiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJzb2xvLmlvIiwic3ViIjoiYm9iIiwiZXhwIjoyMDczNjcwNDgyLCJuYmYiOjE3NjYwODY0ODIsImlhdCI6MTc2NjA4NjQ4Mn0.ZHAw7nbANhnYvBBknN9_ORCQZ934Vv_vAelx8odC3bsC5Yesif7ZSsnEp9zFjGG6wBvvV3LrtuBuWx9mTYUZS6rwWUKsvDXyheZXYRmXndOqpY0gcJJaulGGqXncQDkmqDA7ZeJLG1s0a6shMXRs6BbV370mYpu8-1dZdtikyVL3pC27QNei35JhfqdYuMw1fMptTVzypx437l9j2htxqtIVgdWUc1iKD9kNKpkJ5O6SNbi6xm267jZ3V_Ns75p_UjLq7krQIUl1W0mB0ywzosFkrRcyXsBsljXec468hgHEARW2lec8FEe-i6uqRuVkFD-AeXMfPhXzqdwysjG_og"

3. Verify the policy

  1. Send a request without a JWT. The request fails JWT authentication before authorization runs, so you get back a 401 Unauthorized response.

    curl -i http://$INGRESS_GW_ADDRESS:80/headers -H "host: www.example.com"

    Example output:

    HTTP/1.1 401 Unauthorized
  2. Send a request with Bob’s JWT. The token is valid, so authentication succeeds, but the sub claim does not match the Allow expression. You get back a 403 Forbidden response.

    curl -i http://$INGRESS_GW_ADDRESS:80/headers -H "host: www.example.com" \
     -H "Authorization: Bearer $BOB_JWT"

    Example output:

    HTTP/1.1 403 Forbidden
  3. Send a request with Alice’s JWT. The token is valid and the sub claim matches the Allow expression, so the request succeeds.

    curl -i http://$INGRESS_GW_ADDRESS:80/headers -H "host: www.example.com" \
     -H "Authorization: Bearer $ALICE_JWT"

    Example output:

    HTTP/1.1 200 OK

More examples

Combine Allow with Require

An authorization block takes a single action, so to combine an Allow rule with a Require rule, create two AgentgatewayPolicy resources that target the same Gateway. Their rules are combined, and a request must satisfy both.

The following policy adds a Require rule to the Allow policy from the previous section. The Require rule enforces an internal-traffic header on every request, no matter which Allow rules are defined.

kubectl apply -f- <<EOF
apiVersion: agentgateway.dev/v1alpha1
kind: AgentgatewayPolicy
metadata:
  name: authz-require
  namespace: agentgateway-system
spec:
  targetRefs:
  - group: gateway.networking.k8s.io
    kind: Gateway
    name: agentgateway-proxy
  traffic:
    authorization:
      action: Require
      policy:
        matchExpressions:
          - "request.headers['x-internal'] == 'true'"
EOF

With both policies applied, only Alice’s requests that also carry the x-internal: true header succeed.

RequestAllow (jwt.sub == 'alice')Require (x-internal: true)Result
Alice’s JWT, with the headerMatchesSatisfied200
Alice’s JWT, no headerMatchesNot satisfied403
Bob’s JWT, with the headerNo matchSatisfied403
Bob’s JWT, no headerNo matchNot satisfied403

Tip

Use Require when you need to enforce a mandatory condition across all traffic, such as requiring an internal header, a valid JWT group claim, or a specific source address range. Because a failed Require check always denies the request, Require cannot be bypassed by adding another Allow rule.

Deny policies

Warning

The Deny action is available but is not recommended for most use cases. Deny rules are error-prone because they often require double-negative logic. For example, to block traffic from outside a valid group, you might attempt to write an expression like jwt.group != 'eng'. However, if the JWT does not contain a group claim at all, this expression evaluates to false and the rule does not fire — silently allowing requests you intended to block.

Use the Require action instead. Require inverts the logic so you write positive conditions: action: Require with jwt.group == 'eng'. This is clearer and safer. If you must test whether a claim is present, use the has() function, such as has(jwt.group) && jwt.group == 'eng'. For more information, see the CEL variables reference.

If you must use Deny, prefer expressions over request attributes that are always present, such as the request path. The following example blocks access to the /admin path for every client, including clients that a separate Allow rule permits.

kubectl apply -f- <<EOF
apiVersion: agentgateway.dev/v1alpha1
kind: AgentgatewayPolicy
metadata:
  name: authz-deny
  namespace: agentgateway-system
spec:
  targetRefs:
  - group: gateway.networking.k8s.io
    kind: Gateway
    name: agentgateway-proxy
  traffic:
    authorization:
      action: Deny
      policy:
        matchExpressions:
          - "request.path.startsWith('/admin')"
EOF

Restrict access by source address

The source.address variable is an IP-typed value, not a string, so you cannot compare it to a CIDR string directly. Use the cidr() function with containsIP() instead, as shown in the following Require rule.

traffic:
  authorization:
    action: Require
    policy:
      matchExpressions:
        - "cidr('10.0.0.0/8').containsIP(source.address)"

Warning

source.address is the peer address of the connection as the gateway observes it, which is not always the original client address. If the connection passes through a cloud load balancer or another proxy, or if source IP preservation is not configured, source.address is that intermediate address. Verify which address your gateway actually sees before you rely on it, because a Require rule that never matches denies every request.

For Layer 4 network-level filtering on downstream connections, use spec.frontend.networkAuthorization instead.

MCP authorization

You can apply authorization policies specifically to MCP servers using the spec.backend.mcp.authorization field in an AgentgatewayPolicy. This lets you control which clients or JWT token holders can access specific MCP tools.

For a complete guide with examples, see JWT auth for MCP services.

JWT authorization

To use JWT claims in authorization policies, you first need to configure JWT authentication in the same policy using spec.traffic.jwtAuthentication, as shown in Setup and test authorization. Without it, the jwt context does not exist, every expression that references jwt fails to match, and the policy denies all traffic while still reporting as accepted and attached.

After the gateway validates a JWT, the decoded claims are available in CEL expressions as top-level fields on jwt, such as jwt.sub or jwt.group.

For setup instructions and examples, see JWT auth.

For a list of available JWT variables, see the CEL variables reference.

Cleanup

You can remove the resources that you created in this guide.
kubectl delete AgentgatewayPolicy authz-guide -n agentgateway-system
kubectl delete AgentgatewayPolicy authz-require -n agentgateway-system --ignore-not-found
kubectl delete AgentgatewayPolicy authz-deny -n agentgateway-system --ignore-not-found
Was this page helpful?
Agentgateway assistant

Ask me anything about agentgateway configuration, features, or usage.

Note: AI-generated content might contain errors; please verify and test all returned information.

Tip: one topic per conversation gives the best results. Use the + button in the chat header to start a new conversation.

Switching topics? Starting a new conversation improves accuracy.
↑↓ navigate select esc dismiss

What could be improved?

Your feedback helps us improve assistant answers and identify docs gaps we should fix.

Need more help? Join us on Discord: https://discord.gg/y9efgEmppm

Want to use your own agent? Add the Solo MCP server to query our docs directly. Get started here: https://search.solo.io/.