Skip to content

Instantly share code, notes, and snippets.

@rvennam
Last active March 1, 2026 23:43
Show Gist options
  • Select an option

  • Save rvennam/ada1846276682a20d3b7af7c406f0805 to your computer and use it in GitHub Desktop.

Select an option

Save rvennam/ada1846276682a20d3b7af7c406f0805 to your computer and use it in GitHub Desktop.
Workshop: Routing to AWS AgentCore agents through Solo Agent Gateway (Cognito JWT, no proxy)

Workshop: Routing to AWS AgentCore Agents Through Solo Agent Gateway

Overview

Route requests to an AWS Bedrock AgentCore agent through Solo Agent Gateway — no proxy, no custom code, no AWS SDK. The gateway handles authentication to AgentCore using a Cognito JWT stored in a Kubernetes Secret, so clients don't need any AWS credentials or tokens.

  your auth (optional)          Cognito JWT (from K8s Secret)
  API key, OAuth, none          backend.auth.secretRef
        │                              │
        ▼                              ▼
┌──────────┐      ┌──────────────────┐      ┌─────────────────────┐
│          │      │                  │      │                     │
│  Client  ├─────▶│  Agent Gateway   ├─────▶│  AWS Bedrock        │
│          │◀─────┤                  │◀─────┤  AgentCore          │
│          │      │                  │      │                     │
└──────────┘      └──────────────────┘      └─────────────────────┘
                   ├ path rewrite    │       ├ customJWTAuthorizer│
                   ├ host rewrite    │       ├ validates Cognito  │
                   ├ TLS origination │       │  ID token (aud)    │
                   ├ rate limiting   │       ├ invokes agent      │
                   └ CORS, metrics   ┘       └────────────────────┘
                         │
                   ┌─────┴─────┐
                   │  K8s      │
                   │  Secret   │
                   │  (JWT)    │
                   └───────────┘

The client-to-gateway leg uses whatever auth you choose (API keys, OAuth, or none). The gateway-to-AgentCore leg is handled automatically: Agent Gateway reads a Cognito JWT from a Kubernetes Secret and attaches it to every upstream request. AgentCore validates the token and invokes the agent.


Prerequisites

  • An EKS cluster with Solo Enterprise Agent Gateway installed
  • kubectl and aws CLI tools configured
  • An AgentCore agent runtime deployed (you need the ARN)
  • python3, jq, and curl installed

Step 1: Set variables

export AGENT_RUNTIME_ARN="arn:aws:bedrock-agentcore:us-east-1:986112284769:runtime/rvennam_agent-tEJ8OxEBo1"
export AWS_REGION="us-east-1"
export ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)

# URL-encode the ARN for the HTTP path
export ENCODED_ARN=$(python3 -c "from urllib.parse import quote; print(quote('${AGENT_RUNTIME_ARN}', safe=''))")

# Agent Gateway
export GATEWAY_NAME="agentgateway"
export GATEWAY_NS="enterprise-agentgateway"
export GW_ADDR=$(kubectl get svc -n $GATEWAY_NS $GATEWAY_NAME \
  -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}")
export GW_PORT=$(kubectl get svc -n $GATEWAY_NS $GATEWAY_NAME \
  -o jsonpath="{.spec.ports[0].port}")
echo "Gateway: http://${GW_ADDR}:${GW_PORT}"

Step 2: Create a Cognito User Pool and service user

Create a User Pool with an app client and a service user. The service user's ID token will be used for backend authentication to AgentCore.

# Create the User Pool
export POOL_ID=$(aws cognito-idp create-user-pool \
  --pool-name agentcore-workshop \
  --region $AWS_REGION \
  --query 'UserPool.Id' --output text)
echo "Pool ID: $POOL_ID"

# Create an app client with password auth enabled
export CLIENT_RESPONSE=$(aws cognito-idp create-user-pool-client \
  --user-pool-id $POOL_ID \
  --client-name agentcore-client \
  --generate-secret \
  --explicit-auth-flows ALLOW_ADMIN_USER_PASSWORD_AUTH ALLOW_REFRESH_TOKEN_AUTH \
  --region $AWS_REGION)

export COGNITO_CLIENT_ID=$(echo $CLIENT_RESPONSE | jq -r '.UserPoolClient.ClientId')
export COGNITO_CLIENT_SECRET=$(echo $CLIENT_RESPONSE | jq -r '.UserPoolClient.ClientSecret')
echo "Client ID: $COGNITO_CLIENT_ID"

# Create a service user for gateway-to-AgentCore authentication
aws cognito-idp admin-create-user \
  --user-pool-id $POOL_ID \
  --username gateway-service \
  --message-action SUPPRESS \
  --region $AWS_REGION

aws cognito-idp admin-set-user-password \
  --user-pool-id $POOL_ID \
  --username gateway-service \
  --password 'GatewayService1!' \
  --permanent \
  --region $AWS_REGION

echo "Created service user: gateway-service"

Get an ID token

Cognito ID tokens include the aud claim (set to the client ID), which AgentCore's JWT authorizer requires.

# Compute the SECRET_HASH required by Cognito
export SECRET_HASH=$(python3 -c "
import hmac, hashlib, base64
msg = 'gateway-service' + '${COGNITO_CLIENT_ID}'
h = hmac.new('${COGNITO_CLIENT_SECRET}'.encode(), msg.encode(), hashlib.sha256)
print(base64.b64encode(h.digest()).decode())
")

# Authenticate and get the ID token
export TOKEN=$(aws cognito-idp admin-initiate-auth \
  --user-pool-id $POOL_ID \
  --client-id $COGNITO_CLIENT_ID \
  --auth-flow ADMIN_USER_PASSWORD_AUTH \
  --auth-parameters USERNAME=gateway-service,PASSWORD='GatewayService1!',SECRET_HASH=$SECRET_HASH \
  --region $AWS_REGION \
  --query 'AuthenticationResult.IdToken' --output text)

echo "Token: ${TOKEN:0:20}..."

Step 3: Configure JWT authentication on AgentCore

Point AgentCore at the Cognito User Pool's OIDC discovery endpoint. Set allowedAudience to the Cognito client ID (which matches the aud claim in ID tokens).

export COGNITO_ISSUER="https://cognito-idp.${AWS_REGION}.amazonaws.com/${POOL_ID}"

aws bedrock-agentcore update-agent-runtime \
  --agent-runtime-id $(echo $AGENT_RUNTIME_ARN | awk -F'/' '{print $2}') \
  --authorizer-configuration '{
    "customJWTAuthorizer": {
      "discoveryUrl": "'${COGNITO_ISSUER}'/.well-known/openid-configuration",
      "allowedAudience": ["'${COGNITO_CLIENT_ID}'"]
    }
  }' \
  --region $AWS_REGION

Verify:

aws bedrock-agentcore get-agent-runtime \
  --agent-runtime-id $(echo $AGENT_RUNTIME_ARN | awk -F'/' '{print $2}') \
  --region $AWS_REGION \
  --query 'authorizerConfiguration'

Step 4: Store the JWT in a Kubernetes Secret

Store the Cognito ID token in a Secret that Agent Gateway will use for backend authentication.

kubectl create ns agentcore

kubectl create secret generic agentcore-jwt \
  -n agentcore \
  --from-literal=Authorization="Bearer ${TOKEN}"

Note: Cognito ID tokens expire after 1 hour. For this workshop, recreate the Secret when the token expires.

For production, the preferred approach is backend.auth.aws — SigV4 signing with IRSA, which eliminates tokens entirely. This requires agentgateway-enterprise#126 (configurable SigV4 service name) to land. Once available, the config simplifies to:

backend:
  tls: {}
  auth:
    aws:
      implicit:
        service: bedrock-agentcore

No Cognito, no tokens, no rotation — IRSA handles credentials automatically.

Step 5: Create the gateway route to AgentCore

Three resources: an external backend, a TLS + backend auth policy, and an HTTPRoute.

kubectl apply -f - <<EOF
---
# External backend pointing to AgentCore
apiVersion: agentgateway.dev/v1alpha1
kind: AgentgatewayBackend
metadata:
  name: agentcore
  namespace: agentcore
spec:
  static:
    host: bedrock-agentcore.${AWS_REGION}.amazonaws.com
    port: 443
---
# TLS origination + backend authentication
apiVersion: enterpriseagentgateway.solo.io/v1alpha1
kind: EnterpriseAgentgatewayPolicy
metadata:
  name: agentcore-backend
  namespace: agentcore
spec:
  targetRefs:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: agentcore
  backend:
    tls: {}
    auth:
      secretRef:
        name: agentcore-jwt
---
# Route with path + host rewrite
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: agentcore
  namespace: agentcore
spec:
  parentRefs:
    - name: ${GATEWAY_NAME}
      namespace: ${GATEWAY_NS}
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      filters:
        - type: URLRewrite
          urlRewrite:
            hostname: bedrock-agentcore.${AWS_REGION}.amazonaws.com
            path:
              type: ReplaceFullPath
              replaceFullPath: /runtimes/${ENCODED_ARN}/invocations
      backendRefs:
        - name: agentcore
          kind: AgentgatewayBackend
          group: agentgateway.dev
EOF

The backend.auth.secretRef tells Agent Gateway to read the Authorization key from the agentcore-jwt Secret and attach it as the Authorization header on every request to AgentCore. The client never sees or handles the JWT.

Verify:

kubectl get agentgatewaybackend agentcore -n agentcore \
  -o jsonpath='{.status.conditions[0].reason}'; echo
kubectl get httproute agentcore -n agentcore \
  -o jsonpath='{.status.parents[0].conditions[0].reason}'; echo

Both should show Accepted.

Step 6: Test

Send a request (no client auth needed)

curl -s -X POST "http://${GW_ADDR}:${GW_PORT}/" \
  -H "Content-Type: application/json" \
  -d '{"role":"user","content":[{"text":"Hello from Agent Gateway!"}]}' | jq .

The client sends a plain request — no Authorization header. Agent Gateway attaches the Cognito JWT from the Secret and forwards to AgentCore over TLS.

Verify the gateway is attaching the JWT

To confirm that authentication is handled by the gateway (not the client), send a request directly to AgentCore without a token:

# Direct to AgentCore (bypassing gateway) — expect 401
curl -s -o /dev/null -w "HTTP %{http_code}\n" \
  -X POST "https://bedrock-agentcore.${AWS_REGION}.amazonaws.com/runtimes/${ENCODED_ARN}/invocations" \
  -H "Content-Type: application/json" \
  -d '{"role":"user","content":[{"text":"test"}]}'

AgentCore rejects the direct request (no JWT), but the same request through the gateway succeeds because the gateway attaches the JWT.

Step 7: Enterprise policies

Rate limiting

kubectl apply -f - <<EOF
apiVersion: ratelimit.solo.io/v1alpha1
kind: RateLimitConfig
metadata:
  name: agentcore-rate-limit
  namespace: ${GATEWAY_NS}
spec:
  raw:
    descriptors:
    - key: generic_key
      value: counter
      rateLimit:
        requestsPerUnit: 3
        unit: MINUTE
    rateLimits:
    - actions:
      - genericKey:
          descriptorValue: counter
      type: REQUEST
---
apiVersion: enterpriseagentgateway.solo.io/v1alpha1
kind: EnterpriseAgentgatewayPolicy
metadata:
  name: agentcore-rate-limit
  namespace: agentcore
spec:
  targetRefs:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: agentcore
  traffic:
    entRateLimit:
      global:
        rateLimitConfigRefs:
        - name: agentcore-rate-limit
          namespace: ${GATEWAY_NS}
EOF

Test (first 3 succeed, then 429):

for i in $(seq 1 6); do
  CODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST "http://${GW_ADDR}:${GW_PORT}/" \
    -H "Content-Type: application/json" \
    -d '{"role":"user","content":[{"text":"ping"}]}')
  echo "Request $i: HTTP $CODE"
done

CORS

kubectl apply -f - <<EOF
apiVersion: enterpriseagentgateway.solo.io/v1alpha1
kind: EnterpriseAgentgatewayPolicy
metadata:
  name: agentcore-cors
  namespace: agentcore
spec:
  targetRefs:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: agentcore
  traffic:
    cors:
      allowOrigins:
      - exact: "https://your-app.example.com"
      allowMethods:
      - POST
      - OPTIONS
      allowHeaders:
      - Content-Type
      maxAge: 86400s
EOF

Observability

Agent Gateway exports OTel metrics and traces for all routed traffic.

# Gateway proxy logs
kubectl logs -n ${GATEWAY_NS} -l app.kubernetes.io/name=agentgateway -f

# Grafana (if OTel stack is deployed)
kubectl port-forward svc/grafana-prometheus-grafana -n monitoring 3000:80 &

Cleanup

# Kubernetes resources
kubectl delete ns agentcore
kubectl delete ratelimitconfig agentcore-rate-limit -n ${GATEWAY_NS} 2>/dev/null

# Cognito
aws cognito-idp delete-user-pool \
  --user-pool-id $POOL_ID \
  --region $AWS_REGION

Summary

What How
Identity provider Amazon Cognito User Pool (service user)
Backend auth EnterpriseAgentgatewayPolicy with backend.auth.secretRef
Token validation AgentCore customJWTAuthorizer validates Cognito ID tokens
Client auth None required (gateway handles backend auth)
Routing AgentgatewayBackend + HTTPRoute (path/host rewrite)
TLS EnterpriseAgentgatewayPolicy with backend.tls
Custom code None
Docker image None
AWS SDK / IAM roles None (no boto3, no IRSA)
Rate limiting RateLimitConfig + EnterpriseAgentgatewayPolicy
CORS EnterpriseAgentgatewayPolicy with traffic.cors
Observability Built-in OTel metrics/traces
@christian-posta
Copy link

This is amazing. Thanks for sharing @rvennam

To round out this example... so we attach the user token to a secret and auto inject it from agw. This is great for demo. But I want to point out what might happen in a real scenario.

Either

  1. the agent is invoked from some application (mobile, web, desktop, etc). maybe over A2A but it doesn't really matter. the application will handle auth with the user. that is, the application will have the user's identity and JWT. so it's possible the app sends this JWT/ID assertion to agent gateway (agw). In that case, agw can passthrough the token to the agent 1

  2. It's possible the token that the app has from login (previous flow) has incorrect audience to just passthrough the gateway. so the gateway could perform some token exchange / obo flow to re-scope the token. and then send it to AgentCore agent. 2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment