Skip to main content
Version: 1.0.0

Upstream Authentication

Each registered Agent-to-Agent (A2A) agent defines whether and how AISIX authenticates to its upstream. AISIX presents the configured credential, if any, when fetching the agent card or forwarding a JSON-RPC call. Clients authenticate to AISIX with a caller API key, which is never forwarded upstream.

This separation lets each upstream use the authentication scheme it requires while callers keep one AISIX credential. AISIX Cloud and the open-source AISIX gateway support the same modes through different management paths.

Prerequisites

Complete Set Up Agent Gateway for AISIX Cloud or the open-source AISIX gateway. To run the optional end-to-end verification, keep the same shell, gateway, test agent, and temporary Docker network running.

Authentication Modes

Choose the mode that matches what the upstream agent expects:

auth_typeRequired fieldUpstream header
noneNoneNo credential is sent.
bearersecretAuthorization: Bearer <secret>
api_keysecretx-api-key: <secret>

auth_type defaults to none. Leave secret unset in that mode. A nonempty secret is required for bearer and api_key.

Use HTTPS for a credentialed upstream. When a bearer token or API key is configured on an http:// URL, the gateway logs a warning because the credential crosses the network in plaintext.

Configure Upstream Authentication

The following examples add bearer authentication to the setup guide's registered echo-agent. When adapting that entry to a credentialed upstream, also replace its URL. Use api_key instead when the upstream expects x-api-key. The echo agent itself does not validate credentials. To test credential forwarding locally, skip these examples and continue with Optional: Verify with a Local Test Proxy, which supplies its own token and final resource update.

AISIX Cloud

Export the credential, then update the registered agent:

export A2A_AGENT_TOKEN="YOUR_UPSTREAM_TOKEN"

curl -fsS -X PATCH "$AISIX_CP/a2a_agents/$A2A_AGENT_ID" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
--data-binary @- <<EOF | jq
{
"auth_type": "bearer",
"secret": "${A2A_AGENT_TOKEN}"
}
EOF

The credential is write-only: AISIX Cloud encrypts it at rest and does not include it in create, list, or update responses. Sending a new secret rotates the credential. Changing from a credentialed mode to none clears the stored credential.

Open-Source AISIX Gateway

Replace the existing echo-agent entry in a2a_agents with the updated entry below, which references a gateway environment variable. Preserve unrelated agents and collections:

resources.yaml (agent authentication)
a2a_agents:
- name: echo-agent
url: http://aisix-a2a-echo:8080
protocol_version: "1.0"
auth_type: bearer
secret: ${A2A_AGENT_TOKEN}

Set A2A_AGENT_TOKEN in the gateway process environment before loading the file. An unset or empty referenced variable causes validation to fail. If the variable is already available in the running container, validate the complete file and send SIGHUP:

docker exec aisix-quickstart \
aisix validate --resources /etc/aisix/resources.yaml

docker kill --signal HUP aisix-quickstart

Adding or changing a container environment variable requires recreating that container with the new value. See Reload a Resources File for the validation and recreation workflow.

Credential Failures

The upstream credential never crosses to the calling client. The agent card served by AISIX contains the upstream agent's metadata with service URLs rewritten to the gateway, but it does not contain the configured credential.

If an upstream rejects an expired, rotated, or revoked credential, only calls to that agent fail. AISIX returns HTTP 502; the upstream status appears in the JSON-RPC error message, and the upstream response body is not proxied to the caller.

Optional: Verify with a Local Test Proxy

The configuration above defines the credential AISIX should send, but the echo agent from the setup guide accepts requests without authenticating them. It therefore cannot prove that the gateway presented the expected token. For an end-to-end local check, put a small bearer-authenticated reverse proxy in front of the agent and verify both accepted and rejected requests.

The proxy is for local testing only: it uses plain HTTP and removes the bearer token before forwarding an accepted request to the echo agent.

The helper below contains the local proxy setup. Copy it as-is; the remaining steps configure and test AISIX.

Start the local bearer-checking proxy

Export a distinct upstream token, then define and start the test proxy on the existing Docker network:

export A2A_AGENT_TOKEN="a2a-upstream-test-token"

start_a2a_auth_proxy() {
docker rm -f aisix-a2a-auth >/dev/null 2>&1 || true
docker run -d --name aisix-a2a-auth \
--network aisix-a2a \
-e UPSTREAM_TOKEN="$1" \
caddy:2.11.4-alpine \
sh -c 'caddy run --config /dev/stdin --adapter caddyfile <<EOF
:8082 {
@authorized header Authorization "Bearer $UPSTREAM_TOKEN"
handle @authorized {
reverse_proxy aisix-a2a-echo:8080 {
header_up -Authorization
header_up Host {upstream_hostport}
}
}
handle {
respond "upstream authentication failed" 401
}
}
EOF'

for attempt in $(seq 1 30); do
docker logs aisix-a2a-auth 2>&1 |
grep -q "serving initial configuration" && return
sleep 1
done
docker logs aisix-a2a-auth >&2
return 1
}

start_a2a_auth_proxy "$A2A_AGENT_TOKEN"

Configure AISIX to Use the Proxy

Update the existing echo-agent to use http://aisix-a2a-auth:8082, bearer authentication, and A2A_AGENT_TOKEN as its secret.

For AISIX Cloud, update the agent created by the setup guide:

curl -fsS -X PATCH "$AISIX_CP/a2a_agents/$A2A_AGENT_ID" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
--data-binary @- <<EOF | jq
{
"url": "http://aisix-a2a-auth:8082",
"auth_type": "bearer",
"secret": "${A2A_AGENT_TOKEN}"
}
EOF

For an open-source AISIX gateway, replace the existing echo-agent entry and preserve the other resources:

resources.yaml (authenticated echo agent)
a2a_agents:
- name: echo-agent
url: http://aisix-a2a-auth:8082
protocol_version: "1.0"
auth_type: bearer
secret: ${A2A_AGENT_TOKEN}

Add A2A_AGENT_TOKEN to the gateway container environment. Because the setup guide did not start the container with this variable, validate the complete file with the variable and recreate the container. Keep the mounts, ports, and other environment variables from the quickstart. See Reload a Resources File.

For the open-source path, recreating the gateway container disconnects it from the temporary A2A network. Connect the replacement container before sending a test request:

docker network connect aisix-a2a "$AISIX_GATEWAY_CONTAINER"

Verify a Valid Credential

After AISIX Cloud projects the update or the open-source gateway restarts, send a request to the agent:

A2A_RESPONSE=$(curl -fsS -X POST "$AISIX_PROXY/a2a/echo-agent" \
-H "Authorization: Bearer $AISIX_A2A_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "req-auth",
"method": "SendMessage",
"params": {
"message": {
"messageId": "msg-auth",
"role": "ROLE_USER",
"parts": [{"text": "Authenticated through AISIX"}]
}
}
}')

echo "$A2A_RESPONSE" | jq -e \
'.result.task.artifacts[].parts[] |
select(.text == "Authenticated through AISIX")'

The command prints the matching text part. The proxy accepts a request only when it carries the gateway-held token, so this response confirms that AISIX replaced the caller's Authorization header with the upstream credential. The proxy removes that credential before forwarding to the echo agent.

Verify a Rejected Credential

Make the proxy expect a different token without changing the credential in AISIX:

start_a2a_auth_proxy "deliberately-wrong-token"

curl -sS -o /tmp/aisix-a2a-auth-failure.json -w "%{http_code}\n" \
-X POST "$AISIX_PROXY/a2a/echo-agent" \
-H "Authorization: Bearer $AISIX_A2A_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": "req-wrong-auth",
"method": "SendMessage",
"params": {
"message": {
"messageId": "msg-wrong-auth",
"role": "ROLE_USER",
"parts": [{"text": "This call should fail"}]
}
}
}'

jq -e \
'.error.code == -32000 and
.error.message == "upstream A2A request failed: upstream returned HTTP 401"' \
/tmp/aisix-a2a-auth-failure.json

if grep -qF "$A2A_AGENT_TOKEN" /tmp/aisix-a2a-auth-failure.json || \
grep -qF "$AISIX_A2A_KEY" /tmp/aisix-a2a-auth-failure.json; then
echo "credential found in client response" >&2
exit 1
fi

The request prints HTTP 502, the jq command prints true, and the credential check produces no output. AISIX includes the upstream status in a JSON-RPC error but does not proxy the upstream response body.

Restore the expected token and remove the temporary response file:

start_a2a_auth_proxy "$A2A_AGENT_TOKEN"
rm /tmp/aisix-a2a-auth-failure.json

Keep aisix-a2a-auth running while you use this authenticated local setup. Remove it before running the cleanup commands in the setup guide:

docker rm -f aisix-a2a-auth

Next Steps

You have now configured how AISIX authenticates to an upstream agent. Use these guides to control callers and traffic: