Skip to content

feat(a2a): honor auth.oidcDiscoveryUrl for in-cluster JWKS fetch - #99

Merged
izzywdev merged 1 commit into
mainfrom
feat/a2a-server-oidc-discovery
Jul 23, 2026
Merged

feat(a2a): honor auth.oidcDiscoveryUrl for in-cluster JWKS fetch#99
izzywdev merged 1 commit into
mainfrom
feat/a2a-server-oidc-discovery

Conversation

@izzywdev

Copy link
Copy Markdown
Owner

What

Makes the A2A server honor the optional auth.oidcDiscoveryUrl field (frozen in values-interface.schema.json, #96) so it can fetch OIDC discovery + JWKS from an in-cluster URL while still anchoring trust to the public issuer — FuzeFront#364 "Option B".

Behavior (matches the field's normative description)

  • oidcDiscoveryUrl set → fetch the discovery document at that URL, use its jwks_uri for keys (typically http://authentik-server.<ns>.svc.cluster.local:9000/.../.well-known/openid-configuration).
  • unset → derive discovery from oidcIssuerUrl exactly as before (unchanged default path; purely additive).
  • regardless → the token iss is validated against oidcIssuerUrl (NOT the discovery host), aud against audience, caller read from callerClaim. Keys come from in-cluster; trust stays anchored to the public issuer string.

Changes

  • config.py: parse oidcDiscoveryUrlAuthConfig.oidc_discovery_url (default None).
  • runtime.py: _resolve_jwks_url selects override-vs-issuer-derived; _build_verifier takes injectable discovery-fetch / JWKS-client / decoder and enforces explicit iss == oidcIssuerUrl regardless of key source.
  • Unit tests (tests/test_runtime_verifier.py, tests/test_config.py): override used for key fetch when set; falls back when unset; iss rejected when != issuer even with the override; config parsing.

Scope

Consumes the frozen contract — does not touch contracts/a2a/v1/**. Out of scope: #93 values wiring (devops), prod SealedSecrets (human), registration/token-decode.

🤖 Generated with Claude Code

The A2A server now consumes the optional auth.oidcDiscoveryUrl field frozen in
values-interface.schema.json (FuzeFront#364 "Option B"): when set, signing keys
are fetched from that in-cluster discovery URL's jwks_uri, while the token `iss`
claim is STILL validated against the public oidcIssuerUrl. When unset, discovery
is derived from oidcIssuerUrl exactly as before (purely additive).

- config: parse oidcDiscoveryUrl into AuthConfig.oidc_discovery_url (default None)
- runtime._build_verifier: injectable discovery-fetch / JWKS-client / decoder;
  _resolve_jwks_url picks the override vs the unchanged issuer-derived path;
  explicit iss anchoring to oidcIssuerUrl regardless of key source
- unit tests: override used for key fetch when set; falls back when unset; iss
  rejected when != oidcIssuerUrl even with the override; config parsing

Consumes the frozen contract; does not modify contracts/a2a/v1/**.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session-Id: 17fb89fd-3579-433b-a6c4-9c9e7f3ec549
Comment thread agent-templates/a2a/runtime.py
@izzywdev
izzywdev marked this pull request as ready for review July 23, 2026 23:02
@izzywdev
izzywdev merged commit d82c533 into main Jul 23, 2026
30 checks passed
izzywdev added a commit that referenced this pull request Jul 23, 2026
…etch (#102)

Semgrep OSS (dynamic-urllib-use-detected) flagged the urllib fetch of the
config-supplied oidcDiscoveryUrl: urllib also honours file://, so a dynamic
discovery URL could read local files. oidcDiscoveryUrl is trusted operator
config, but guarding the scheme up front is cheap, correct hardening.

- runtime._require_http_url: reject any URL whose scheme is not in {http, https}
  (file://, ftp://, schemeless) with the existing discovery-config-error path.
- _resolve_jwks_url guards oidcDiscoveryUrl BEFORE the fetch (fetch never happens
  for a bad scheme) and constrains the discovery doc's jwks_uri to http(s) too.
- _http_get_json guards the URL before urllib.urlopen (defense in depth).
- Behavior identical for valid http/https URLs.

Unit tests: file://ftp://schemeless oidcDiscoveryUrl raises the config error and
the fetch/JWKS-client never runs; a file:// jwks_uri from discovery is rejected.

Follow-up to #99. Consumes the frozen contract; contracts/a2a/v1/** untouched.


Claude-Session-Id: 17fb89fd-3579-433b-a6c4-9c9e7f3ec549

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants