1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# Authenticating an agent's HTTP surface.
#
# One config with every `[server.auth]` shape written out: the JWT block is live,
# the other three are commented alternatives. Swap whichever you want in — only
# one `[server.auth]` can be active, since `type` selects the whole block.
#
# a2a validate --config a2a-agents/examples/auth.toml
# JWT_SECRET=dev-secret a2a run --config a2a-agents/examples/auth.toml
#
# Needs the `auth` feature: `cargo run -p a2a-agents --features auth --bin a2a`.
# Secrets are environment references so the config can be committed; `a2a
# validate` checks the shape without them, `a2a run` requires them to resolve.
#
# One gotcha this file demonstrates: an environment reference inside a *comment*
# still counts. The scanner reads the raw text — it has to, since the allowlist
# is checked before the config is parsed — so `a2a validate` lists the commented
# blocks' variables below as unset, and a control plane would require them under
# `--allow-env`. Delete the alternatives you are not using.
[]
= "Secure Agent"
= "An agent that requires a credential."
[]
= "echo"
[]
= "127.0.0.1"
= 8080
[]
= "inmemory"
# --- JWT --------------------------------------------------------------------
# Verifies a bearer JWT. HMAC (HS*) takes a shared `secret`; RSA (RS*) takes the
# path to a public key. `issuer` and `audience` are optional and checked only
# when set — leaving them out accepts any token the key validates, which is
# rarely what a production deployment wants.
[]
= "jwt"
= "${JWT_SECRET}"
= "HS256" # HS256 | HS384 | HS512 | RS256 | RS384 | RS512
= "https://auth.example.com"
= "api://my-agent"
# RSA instead of a shared secret — replace `secret` with:
# rsa_pem_path = "/etc/agent/jwt-public.pem"
# algorithm = "RS256"
# --- Bearer tokens ----------------------------------------------------------
# A fixed list of accepted tokens. No expiry, no revocation, no claims: fine for
# a service-to-service link you control, wrong for anything user-facing.
#
# [server.auth]
# type = "bearer"
# tokens = ["${AGENT_TOKEN}"]
# format = "JWT" # advertised on the agent card, optional
# --- API key ----------------------------------------------------------------
# Same trade-off as bearer, carried somewhere else. `location` is header, query,
# or cookie; `name` is the header/param it is read from.
#
# [server.auth]
# type = "api_key"
# keys = ["${AGENT_API_KEY}"]
# location = "header"
# name = "X-API-Key"
# --- OAuth2 -----------------------------------------------------------------
# `flow = "authorization_code"` is the interactive flow and needs `redirect_url`;
# `flow = "client_credentials"` is machine-to-machine and ignores it (the
# `authorization_url` is still required by the schema and unused).
#
# Endpoints for common providers:
# GitHub https://github.com/login/oauth/authorize
# https://github.com/login/oauth/access_token
# Google https://accounts.google.com/o/oauth2/v2/auth
# https://oauth2.googleapis.com/token
# Azure AD https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize
# https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
# Keycloak https://{host}/realms/{realm}/protocol/openid-connect/auth
# https://{host}/realms/{realm}/protocol/openid-connect/token
#
# [server.auth]
# type = "oauth2"
# client_id = "${OAUTH_CLIENT_ID}"
# client_secret = "${OAUTH_CLIENT_SECRET}"
# authorization_url = "https://provider.example.com/oauth/authorize"
# token_url = "https://provider.example.com/oauth/token"
# flow = "authorization_code" # or "client_credentials"
# redirect_url = "http://localhost:8080/oauth/callback"
# scopes = ["read", "write"]
[[]]
= "secure-echo"
= "Secure echo"
= "Echoes a message back, for callers holding a valid credential."
= ["hello"]