Skip to content

Instantly share code, notes, and snippets.

@rvennam
Created February 19, 2026 18:27
Show Gist options
  • Select an option

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

Select an option

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

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. AgentCore validates JWTs issued by an Amazon Cognito User Pool, and Agent Gateway forwards them directly.

┌──────────┐      ┌──────────────────┐      ┌─────────────────────┐
│          │      │                  │      │                     │
│  Client  ├─JWT─▶│  Agent Gateway   ├─────▶│  AWS Bedrock        │
│          │◀─────┤  (path + host    │◀─────┤  AgentCore          │
│          │      │   rewrite, TLS)  │      │  (validates JWT,    │
│          │      │                  │      │   runs agent)       │
└──────────┘      └──────────────────┘      └─────────────────────┘

Everything stays inside AWS. Cognito issues JWTs, AgentCore validates them, Agent Gateway routes and applies enterprise policies.


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)
  • 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

Create a User Pool, domain, resource server, and app client. The client credentials flow requires all four.

# 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"

# Add a domain (required for the token endpoint)
aws cognito-idp create-user-pool-domain \
  --user-pool-id $POOL_ID \
  --domain "agentcore-workshop-${ACCOUNT_ID}" \
  --region $AWS_REGION

export COGNITO_DOMAIN="agentcore-workshop-${ACCOUNT_ID}"
echo "Token endpoint: https://${COGNITO_DOMAIN}.auth.${AWS_REGION}.amazoncognito.com/oauth2/token"

# Create a resource server (defines the scope for client credentials)
aws cognito-idp create-resource-server \
  --user-pool-id $POOL_ID \
  --identifier "agentcore" \
  --name "AgentCore API" \
  --scopes ScopeName=invoke,ScopeDescription="Invoke AgentCore agents" \
  --region $AWS_REGION

# Create an app client with client_credentials grant
export CLIENT_RESPONSE=$(aws cognito-idp create-user-pool-client \
  --user-pool-id $POOL_ID \
  --client-name agentcore-client \
  --generate-secret \
  --allowed-o-auth-flows client_credentials \
  --allowed-o-auth-scopes agentcore/invoke \
  --allowed-o-auth-flows-user-pool-client \
  --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"

Test token issuance

TOKEN=$(curl -s -X POST \
  "https://${COGNITO_DOMAIN}.auth.${AWS_REGION}.amazoncognito.com/oauth2/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -u "${COGNITO_CLIENT_ID}:${COGNITO_CLIENT_SECRET}" \
  -d "grant_type=client_credentials&scope=agentcore/invoke" \
  | jq -r .access_token)

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

Step 3: Configure JWT authentication on AgentCore

Point AgentCore at the Cognito User Pool's OIDC discovery endpoint.

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

Note: Cognito access tokens from the client_credentials flow do not include a standard aud claim. If AgentCore rejects the token, try setting allowedAudience to the resource server identifier (agentcore) or omit it if the API supports that.

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: Create the gateway route to AgentCore

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

kubectl create ns agentcore

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
apiVersion: enterpriseagentgateway.solo.io/v1alpha1
kind: EnterpriseAgentgatewayPolicy
metadata:
  name: agentcore-tls
  namespace: agentcore
spec:
  targetRefs:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: agentcore
  backend:
    tls: {}
---
# 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

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 5: Test

Get a fresh token

TOKEN=$(curl -s -X POST \
  "https://${COGNITO_DOMAIN}.auth.${AWS_REGION}.amazoncognito.com/oauth2/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -u "${COGNITO_CLIENT_ID}:${COGNITO_CLIENT_SECRET}" \
  -d "grant_type=client_credentials&scope=agentcore/invoke" \
  | jq -r .access_token)

Send a request

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

Verify auth enforcement

# No token — expect 401
curl -s -o /dev/null -w "HTTP %{http_code}\n" \
  -X POST "http://${GW_ADDR}:${GW_PORT}/" \
  -H "Content-Type: application/json" \
  -d '{"role":"user","content":[{"text":"test"}]}'

# Invalid token — expect 403
curl -s -o /dev/null -w "HTTP %{http_code}\n" \
  -X POST "http://${GW_ADDR}:${GW_PORT}/" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer invalid.jwt.token" \
  -d '{"role":"user","content":[{"text":"test"}]}'

Step 6: 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" \
    -H "Authorization: Bearer $TOKEN" \
    -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
      - Authorization
      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-domain \
  --user-pool-id $POOL_ID \
  --domain $COGNITO_DOMAIN \
  --region $AWS_REGION
aws cognito-idp delete-user-pool \
  --user-pool-id $POOL_ID \
  --region $AWS_REGION

Summary

What How
Identity provider Amazon Cognito User Pool (client credentials flow)
Authentication AgentCore customJWTAuthorizer validates Cognito JWTs
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment