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.
- An EKS cluster with Solo Enterprise Agent Gateway installed
kubectlandawsCLI tools configured- An AgentCore agent runtime deployed (you need the ARN)
jqandcurlinstalled
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}"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"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}..."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_REGIONNote: Cognito access tokens from the client_credentials flow do not include a standard
audclaim. If AgentCore rejects the token, try settingallowedAudienceto 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'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
EOFVerify:
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}'; echoBoth should show Accepted.
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)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 .# 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"}]}'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}
EOFTest (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"
donekubectl 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
EOFAgent 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 &# 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| 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 |