Apply Policy with Agentgateway
In this final post, we’ll explore how to use Agentgateway as a policy enforcement point for AAuth enabled agent communication. Throughout this demo series, we’ve seen how AAuth provides identity and authorization for agents. Now we’ll see how Agentgateway enables centralized policy enforcement, observability, and traffic management for agent-to-agent communication.
Watch the demo
Why Agentgateway for AAuth?
Agentgateway sits between agent calls and provides several critical capabilities:
- AAuth Protocol Enforcement - Verify message signatures and agent identity before reaching your services
- Authorization Policy - Apply fine-grained access control using CEL expressions on AAuth claims (could call out to an external policy engine like OPA or OpenFGA)
- Observability - Add structured logging and distributed tracing with AAuth metadata
- Rate Limiting - Protect resources from abuse while maintaining agent identity context, and honoring/requiring progressive auth
- Centralized Configuration - Manage policies across all agent communication in one place
sequenceDiagram
participant Client as Agent Client
participant AGW as AgentGateway
participant Backend as Agent Service
Client->>AGW: 1. Signed Request
AGW->>AGW: 2. Verify Signature
AGW->>AGW: 3. Check Authorization
AGW->>Backend: 4. Forward if Allowed
Backend-->>AGW: 5. Response
AGW-->>Client: 6. Response
Running with Policy Enforcement
If you’ve followed the pre-requisites for this demo, you can change the Agentgateway command to the following:
> cd Agentgateway
> ./agw -f config-policy.yaml
Watch the Agentgateway logs to see policy enforcement:
info request aauth.scheme=Jwt aauth.agent=http://supply-chain-agent.localhost:3000
jwt_act={agent:http://backend.localhost:8000,sub:00b519e8-f409...}
authenticated=true user_id=mcp-user http.status=200
Let’s take a look at the configuration:
AAuth Policy Configuration
Let’s examine the key sections of the Agentgateway configuration for AAuth policy enforcement. You can see the full configuration in the source code of this documentation.
1. Observability: Logging and Tracing
The first step in policy enforcement is visibility. Agentgateway can extract AAuth claims and add them to logs and traces:
config:
tracing:
otlpEndpoint: http://localhost:4317
randomSampling: true
fields:
add:
authenticated: 'jwt.sub != null'
jwt_act: 'jwt.act'
user_id: 'jwt.name'
token_issuer: 'jwt.iss'
token_audience: 'jwt.aud'
aauth_scheme: 'aauth.scheme'
aauth_agent_identity: 'aauth.agent'
aauth_jwt_claims: 'aauth.jwt_claims'
logging:
fields:
add:
authenticated: 'jwt.sub != null'
jwt_act: "jwt.act"
user_id: 'jwt.name'
aauth_scheme: 'aauth.scheme'
aauth_agent_identity: 'aauth.agent'
Key Fields:
aauth.scheme- Which AAuth scheme was used (JWKS, JWT, HWK)aauth.agent- The verified identity of the calling agentjwt.act- The actor claim for delegation chains (on-behalf-of)jwt.sub- The user identity when using user-delegated authorizationaauth.jwt_claims- Full JWT claims for advanced policy decisions
When you run the demo with this configuration, you’ll see rich logs like:
info request aauth.scheme=Jwks aauth.agent=http://backend.localhost:8000
http.method=POST http.status=200 trace.id=3aef7d77d62861bad0066c70fc24a1db
duration=117ms authenticated=false
2. AAuth Protocol Enforcement
The aauth policy controls how Agentgateway validates incoming requests:
listeners:
- hostname: "supply-chain-agent.localhost"
routes:
- policies:
aauth:
mode: optional
requiredScheme: jwks
timestampTolerance: 60
Configuration Options:
mode: optional- Allow requests without AAuth (for testing), usestrictin productionrequiredScheme: jwks- Enforce that agents use JWKS for identity (not HWK pseudonymous)timestampTolerance: 60- Allow 60 seconds clock skew for signature verification
This ensures that:
- Messages are signed with the correct HTTP signatures
- Agent identity can be verified via JWKS discovery
- Signatures are recent (replay attack protection)
3. Authorization Policies with CEL
The authorization policy uses Common Expression Language (CEL) to evaluate access control rules based on AAuth claims:
authorization:
rules:
# Rule 1: Verify agent identity and JWT claims
- "aauth.agent == 'http://backend.localhost:8000' &&
(!has(aauth.jwt_claims) ||
(has(aauth.jwt_claims.aud) &&
aauth.jwt_claims.aud == 'http://supply-chain-agent.localhost:3000' &&
aauth.jwt_claims.scope.contains('supply-chain:optimize')))"
# Rule 2: Allow well-known endpoints
- "request.path.startsWith('/.well-known/')"
# Rule 3: Allow JWKS discovery
- "request.path.startsWith('/jwks.json')"
Rule 1 Breakdown:
aauth.agent == 'http://backend.localhost:8000'- Only allow the backend agent!has(aauth.jwt_claims)- Allow signature-only mode (no auth token)aauth.jwt_claims.aud == 'http://supply-chain-agent.localhost:3000'- Verify token audienceaauth.jwt_claims.scope.contains('supply-chain:optimize')- Verify required scope
This policy allows requests if ANY rule evaluates to true. You can make policies more restrictive by requiring multiple conditions.
4. Checking the act Claim for Delegation
When agents call other agents on-behalf-of users (token exchange), the act claim contains the delegation chain. You can write policies to validate this:
authorization:
rules:
# Verify delegation chain
- "has(aauth.jwt_claims.act) &&
aauth.jwt_claims.act.agent == 'http://backend.localhost:8000' &&
aauth.jwt_claims.act.sub != null"
This rule ensures:
- The token was obtained via token exchange (
actclaim present) - The original caller was the backend agent
- A user identity is present (user-delegated authorization)
From the token exchange post, recall that the act claim looks like:
{
"agent": "http://supply-chain-agent.localhost:3000",
"act": {
"agent": "http://backend.localhost:8000",
"sub": "00b519e8-f409-4201-8911-1cb408e8a082"
}
}
You can validate the entire delegation chain:
# Only allow supply-chain-agent when acting on behalf of backend + user
- "aauth.agent == 'http://supply-chain-agent.localhost:3000' &&
has(aauth.jwt_claims.act) &&
aauth.jwt_claims.act.agent == 'http://backend.localhost:8000' &&
has(aauth.jwt_claims.act.sub)"
5. Rate Limiting (Optional)
While not shown in the demo configuration, Agentgateway supports rate limiting per agent:
rateLimit:
requestsPerMinute: 100
burstSize: 20
keyExtractor: 'aauth.agent' # Rate limit by agent identity
This allows you to:
- Prevent agent abuse while maintaining identity context
- Apply different limits to different agents
- Track which agent is consuming resources
Complete Policy Configuration Example
Here’s a production-ready configuration from config-policy.yaml:
binds:
- port: 3000
listeners:
- hostname: "supply-chain-agent.localhost"
routes:
- policies:
# AAuth verification
aauth:
mode: optional # Use 'required' in production
requiredScheme: jwks
timestampTolerance: 60
# Authorization rules
authorization:
rules:
- "aauth.agent == 'http://backend.localhost:8000' &&
(!has(aauth.jwt_claims) ||
(has(aauth.jwt_claims.aud) &&
aauth.jwt_claims.aud == 'http://supply-chain-agent.localhost:3000' &&
aauth.jwt_claims.scope.contains('supply-chain:optimize')))"
- "request.path.startsWith('/.well-known/')"
- "request.path.startsWith('/jwks.json')"
# Mark as agent-to-agent traffic
a2a: {}
# Pass auth headers to backend
backendAuth:
passthrough: {}
# CORS for browser-based agents
cors:
allowOrigins: ["*"]
allowHeaders: ["*"]
backends:
- host: localhost:9999
Testing Policy Violations
Try these to see policy enforcement in action:
1. Wrong agent identity: Change the backend’s agent identifier and watch the authorization rule fail:
ERROR: Authorization denied: aauth.agent 'http://wrong-agent' not in allowed list
2. Missing required scope: Modify the resource token to request a different scope and see the authorization fail:
ERROR: Authorization denied: required scope 'supply-chain:optimize' not found
3. Invalid audience: Send a token meant for a different service:
ERROR: Authorization denied: aud claim mismatch
Summary
Agentgateway provides a centralized policy enforcement point for AAuth-enabled agent communication:
| Capability | Benefit |
|---|---|
| Signature Verification | Verify agent identity before reaching your services |
| Authorization Policy | Fine-grained access control using CEL on AAuth claims |
| Delegation Validation | Track and validate on-behalf-of chains with act claim |
| Observability | Structured logging and tracing with agent identity context |
| Rate Limiting | Protect resources while maintaining identity context |
| Centralized Config | Manage all agent policies in one place |
Key Policy Patterns:
- Verify agent identity:
aauth.agent == 'expected-agent' - Check authorization scopes:
aauth.jwt_claims.scope.contains('required-scope') - Validate delegation:
has(aauth.jwt_claims.act) && aauth.jwt_claims.act.agent == 'trusted-agent' - Require user consent:
has(aauth.jwt_claims.sub)
This completes our deep dive into AAuth! You now understand:
- Agent identity establishment with JWKS
- Autonomous and user-delegated authorization flows
- Token exchange for delegation chains
- Policy enforcement with Agentgateway
For more advanced scenarios, explore the AAuth specification and consider integrating with OPA or FGA for complex authorization logic.