JWT Authorization
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 -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.0/standard-install.yaml
# agentgateway CRDs
helm upgrade -i --create-namespace \
--namespace agentgateway-system \
--version v2.2.1 agentgateway-crds oci://ghcr.io/kgateway-dev/charts/agentgateway-crds
# Control plane
helm upgrade -i -n agentgateway-system agentgateway oci://ghcr.io/kgateway-dev/charts/agentgateway \
--version v2.2.1Step 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