Repository navigation
feat(a2a): authenticate to agents with per-agent credentials, with an a2a-auth example - #1542
Merged
Merged
Conversation
An agent entry in agents.yaml can now carry an auth block: a static bearer token (token_env) or an OIDC client-credentials grant (oidc). The block names the environment variables that hold the secrets, so the file, the logs and infer agents list never contain them. The credentials ride on a transport inside NewClient, so they reach every request to the agent: the card fetch, SendMessage, GetTask polling, CancelTask and the liveness probe. They are only sent to the origin of the configured agent, never to a redirect target or another port on the host. An OIDC token is fetched once per grant and refreshed before it expires. A 401 or 403, a missing variable or a failed token request reaches the model as "Authentication failed for A2A agent <name>". Background polling ends the job on it instead of retrying forever. Closes #1514
The A2A protocol has the agent declare how to authenticate in the securitySchemes of its card, so the issuer does not belong in agents.yaml. The grant now takes its token endpoint from the card: the discovery URL of an openIdConnect scheme, or the token URL of an oauth2 client-credentials flow, with the scopes the card's security requirements list. auth.oidc keeps the client id, the secret variable and the audience. issuer_url becomes an optional pin. With it set, a card that points outside that issuer is refused, so a tampered card cannot collect the client secret. Refs #1514
…heme An agent whose card has no securitySchemes cannot say where its tokens are issued. The mock agent is one of them today. auth.oidc.issuer_url now also serves as the fallback issuer for such a card, next to pinning the issuer a declaring card may point at. Without it the request fails with an authentication error that says to set it. Refs #1514
Two mock agents protect /a2a, one with a static bearer token and one with OIDC against Keycloak. infer authenticates to each with the credentials in the example's agents.yaml, with the secrets read from the environment. The model is a tokenless mock, so no provider API key is needed. The Taskfile covers an authorized delegation to each agent, a wrong token, a missing token and a wrong client secret, the last three reported as authentication failures. Closes #1512
…2a-auth mock-agent 0.5.0 declares its runtime auth scheme on the agent card, so the OIDC agent's card now points at the Keycloak realm. Drop issuer_url from the example's agents.yaml so it shows the card-driven path, and pin the image to 0.5.0, since an older cached latest has no scheme and needs issuer_url.
5 tasks
Contributor
|
🎉 This PR is included in version 0.226.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Lets
inferauthenticate to A2A agents that protect/a2a. An agent entry inagents.yamlcan carry anauthblock with a static bearer token or an OIDC client-credentials grant.Closes #1514 and closes #1512, which adds
examples/a2a-authon top of it.How it works
authblock names environment variables and never holds a value.LoadYAMLexpands${VAR}over the whole file and everyinfer agents add/update/removewrites the result back, so atoken: ${VAR}field would have ended up on disk in plain text. With variable names, the file,infer agents listandinfer agents showcannot leak a secret.a2ainfra.NewClient, the one constructor behind the agent card fetch,SendMessage,GetTaskpolling,CancelTask, the liveness probe andinfer agents status. ADKConfig.Headersdoes not reach the card fetch, a transport does.agent_urlcomes from the model, so the transport only authenticates requests to the scheme, host and port of the configured agent. A redirect target or another port on the same host gets nothing.securitySchemesof its card. The CLI reads the token endpoint from there: the discovery URL of anopenIdConnectscheme, or the token URL of anoauth2client-credentials flow, with the scopes the card'ssecurityRequirementslist.agents.yamlonly holds the client id, the secret variable and an optional audience.issuer_url, pin and fallback. The client secret goes to whatever endpoint the card names, and the card is fetched unauthenticated. Withauth.oidc.issuer_urlset, a card that points outside that issuer is refused before the secret is sent. The same field is the fallback for a card that declares no scheme at all, as agents generated before ADL CLI v0.67.0 do.clientcredentialstoken source per grant lives for the process, fetches once and refreshes shortly before expiry. This addsgolang.org/x/oauth2as a direct dependency.Authentication failed for A2A agent "<name>": ..., without the response body. Background polling ends the job on it instead of retrying forever.Example (#1512)
examples/a2a-auth/runs onemock-agent0.5.0 behindA2A_AUTH_TOKENand one behind OIDC against Keycloak, with a tokenless mock model and no provider API key. The credentials are in the example's.infer/agents.yaml, with the secrets read from the environment. Each agent declares its scheme on its card, so the OIDC entry only holds the client andinferreads the token endpoint from the card.task tokentask oidctask wrong-tokenAuthentication failed for A2A agent "token-agent": the agent rejected the request with status 401task missing-tokenAuthentication failed for A2A agent "token-agent": environment variable TOKEN_AGENT_TOKEN is not settask wrong-secretAuthentication failed for A2A agent "oidc-agent": fetching the OIDC token: the token endpoint answered with status 401 unauthorized_clientKeycloak and the OIDC agent share a network namespace, so the issuer is
http://localhost:8090/realms/a2aforinferon the host and for the agent in its container.docs/examples.mdlists the example anddocs/agents-configuration.mdlinks it.Testing
infrastructure/auth_test.go: the header on the card fetch,SendTask,GetTaskandCancelTask, no header on another origin or after a cross-origin redirect, the OIDC token endpoint read from anopenIdConnectand anoauth2card scheme, one token fetch across requests plus a refetch once the token expires, a card with no usable scheme or outside the pinned issuer refused before the secret is sent, the auth-failure message for 401 and 403, an unset variable.config/agents_test.go: origin matching and a load and rewrite ofagents.yamlthat never contains the secret.submit_task_error_handling_test.go: a rejected submission and a rejected poll both end with the named error.task statusrun againstmock-agent0.5.0 and Keycloak 26.3.2 with a local build, with noissuer_urlconfigured.task precommit:runpasses.Not in this PR
infer agents add/updateflags forauth. Editagents.yamldirectly for now.INFER_A2A_AGENTSwith noagents.yamlentry.inference-gateway/adkwould remove the text match and the roughly 6 second retry before a credential error surfaces on task requests.Affected repos
inference-gateway/docsneeds the newauthblock documented, tracked in [DOCS] Document per-agent A2A credentials in the CLI docs#985.inference-gateway/adl-cli: generated Go agents did not declare their runtime auth mode in the card'ssecuritySchemes. Fixed in feat(go): declare the runtime security scheme on the agent card adl-cli#483 (closes [FEATURE] Declare securitySchemes on the agent card for every runtime auth mode adl-cli#482), released in v0.67.0.inference-gateway/mock-agent: its hand-maintainedmain.gowas re-synced with the template in feat: declare the runtime security scheme on the agent card mock-agent#172, released in 0.5.0, which the example pins.inference-gateway/adkcould expose a typed HTTP status error (optional follow-up, see above).