For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.
JWT authorization
Secure agentgateway with JWT authentication and fine-grained access control on Kubernetes
Secure your agentgateway LLM endpoints with JWT authentication using TrafficPolicy on Kubernetes.
What you’ll build
In this tutorial, you will:
- Set up a local Kubernetes cluster with agentgateway and an LLM backend
- Create a TrafficPolicy with JWT authentication
- Test that unauthenticated requests are rejected
- Test that authenticated requests with a valid JWT are allowed
Before you begin
Make sure you have the following tools installed:
- Docker
- kubectl
- kind
- Helm
- An OpenAI API key (get one at platform.openai.com)
For detailed installation instructions, see the LLM Gateway tutorial.
Step 1: Create a kind cluster
kind create cluster --name agentgatewayStep 2: Install agentgateway
# Gateway API CRDs
kubectl apply --server-side -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.5.0/standard-install.yaml
# agentgateway CRDs
helm upgrade -i --create-namespace \
--namespace agentgateway-system \
--version v agentgateway-crds oci://cr.agentgateway.dev/charts/agentgateway-crds
# Control plane
helm upgrade -i -n agentgateway-system agentgateway oci://cr.agentgateway.dev/charts/agentgateway \
--version vStep 3: Create a Gateway
kubectl apply -f- <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: agentgateway-proxy
namespace: agentgateway-system
spec:
gatewayClassName: agentgateway
listeners:
- protocol: HTTP
port: 80
name: http
allowedRoutes:
namespaces:
from: All
EOFWait for the proxy:
kubectl get deployment agentgateway-proxy -n agentgateway-systemStep 4: Set up an LLM backend
Set your API key and create the secret and backend:
export OPENAI_API_KEY=<insert your API key>
kubectl apply -f- <<EOF
apiVersion: v1
kind: Secret
metadata:
name: openai-secret
namespace: agentgateway-system
type: Opaque
stringData:
Authorization: $OPENAI_API_KEY
---
apiVersion: agentgateway.dev/v1alpha1
kind: AgentgatewayBackend
metadata:
name: openai
namespace: agentgateway-system
spec:
ai:
provider:
openai:
model: gpt-4.1-nano
policies:
auth:
secretRef:
name: openai-secret
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: openai
namespace: agentgateway-system
spec:
parentRefs:
- name: agentgateway-proxy
namespace: agentgateway-system
rules:
- backendRefs:
- name: openai
namespace: agentgateway-system
group: agentgateway.dev
kind: AgentgatewayBackend
EOFStep 5: Download the test JWT keys
For this tutorial, use pre-generated test keys from the agentgateway repository:
# Download the JWKS public key
curl -sL https://raw.githubusercontent.com/agentgateway/agentgateway/main/manifests/jwt/pub-key -o pub-key
# Download a pre-generated test JWT token
curl -sL https://raw.githubusercontent.com/agentgateway/agentgateway/main/manifests/jwt/example1.key -o test-token.jwtThe test token contains these claims:
{
"iss": "solo.io",
"sub": "alice",
"exp": 1900650294
}Step 6: Create a JWT TrafficPolicy
Create a TrafficPolicy that enforces JWT authentication on the Gateway. Requests without a valid JWT will be rejected.
# Read the JWKS into a variable
JWKS=$(cat pub-key)
kubectl apply -f- <<EOF
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TrafficPolicy
metadata:
name: jwt-auth
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: '$JWKS'
authorization:
action: Allow
policy:
matchExpressions:
- 'jwt.sub == "alice"'
EOFThis policy:
- Requires a valid JWT on every request (
mode: Strict) - Validates the token against the provided JWKS public key
- Authorizes only tokens where
subisalice
Step 7: Test the JWT authentication
Set up port-forwarding:
kubectl port-forward deployment/agentgateway-proxy -n agentgateway-system 8080:80 &Test without a token (should fail)
curl -s http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{"model": "gpt-4.1-nano", "messages": [{"role": "user", "content": "Hello"}]}'You should receive a 401 Unauthorized response.
Test with a valid token (should succeed)
curl -s http://localhost:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $(cat test-token.jwt)" \
-d '{"model": "gpt-4.1-nano", "messages": [{"role": "user", "content": "Hello! What is JWT?"}]}' | jqYou should receive a successful response from OpenAI.
Authorization rules
The matchExpressions field uses CEL (Common Expression Language) for fine-grained access control:
# Allow only a specific user
matchExpressions:
- 'jwt.sub == "alice"'
# Allow by email domain
matchExpressions:
- 'jwt.email.endsWith("@company.com")'
# Allow by role claim
matchExpressions:
- '"admin" in jwt.roles'Cleanup
kill %1 2>/dev/null
kind delete cluster --name agentgateway