Skip to content

feat: add per-provider OIDC discovery URL override - #2386

Merged
benmcclelland merged 1 commit into
mainfrom
sis/oidc-discovery-urls
Sep 14, 2026
Merged

benmcclelland merged 1 commit into
mainfrom
sis/oidc-discovery-urls

Conversation

@niksis02

Copy link
Copy Markdown
Contributor

AssumeRoleWithWebIdentity always fetched a provider's discovery document from <provider url>/.well-known/openid-configuration, so an identity provider that issues tokens naming a public issuer while serving its metadata and keys on a cluster-internal path could not be used: reaching it meant relaxing the endpoint checks for every registered provider. --oidc-discovery-url moves that one fetch to an operator-named endpoint, which is how keys can be looked up over an optimized private path while the tokens themselves stay verifiable from the public internet against the issuer alone, as the JWT spec requires.

The flag takes <provider url>=<discovery url> pairs, can be repeated once per provider, and is also read from VGW_IAM_OIDC_DISCOVERY_URLS as a comma-separated list; the Helm chart exposes the same list as iamServer.oidc.discoveryUrls. The discovery URL is fetched exactly as written, so it must carry the /.well-known/openid-configuration path when the provider serves it there. A malformed pair is rejected at startup rather than at the first assume-role call.

Only the fetch moves. The provider URL is still what a token's iss claim is matched against, the fetched document's own issuer field must still equal it, and the key set still comes from the jwks_uri that document publishes. A configured discovery endpoint is named by the operator at startup rather than by a request, so it and the jwks_uri it publishes waive the private-address check for that provider's fetch chain only, without --oidc-allow-private-endpoints and its far broader effect on every other provider. Transport rules are unchanged: a plaintext discovery URL still requires --oidc-allow-insecure-transport.

Thumbprint auto-fetch follows the override and pins the discovery endpoint's certificate chain, since that is the host every later fetch is verified against.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Three moderate findings remain regarding cache identity, URL validation, and parsing provider URLs containing =.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds per-provider OIDC discovery URL overrides for IAM, including JWKS and thumbprint fetching through configured endpoints while preserving issuer validation.

Changes:

  • Adds startup parsing and endpoint resolution.
  • Updates OIDC discovery, JWKS, and thumbprint fetching.
  • Exposes configuration through CLI, environment, embedded IAM, Helm, tests, and documentation.
File summaries
File Summary Final review comments
iamapi/server.go Parses discovery URL mappings. Moderate (3 votes): validate both URLs structurally at startup. Moderate (1 vote): support = in provider URL paths when parsing pairs.
iamapi/server_test.go Tests parsing and startup validation. No final comments.
iamapi/internal/iamutil/webidentity.go Uses overridden discovery endpoints. Moderate (1 vote): include the resolved discovery target in cache and singleflight identity, or invalidate caches when it changes.
iamapi/internal/iamutil/oidc.go Resolves OIDC endpoints and policies. No final comments.
iamapi/internal/iamutil/oidc_thumbprint.go Fetches thumbprints from override endpoints. No final comments.
iamapi/internal/iamutil/oidc_thumbprint_test.go Tests override thumbprint behavior. No final comments.
iamapi/internal/iamutil/oidc_test.go Tests discovery resolution. No final comments.
iamapi/controller.go Applies overrides during provider creation. No final comments.
iamapi/controller_test.go Tests end-to-end OIDC overrides. No final comments.
extra/example-iam.conf Documents environment configuration. No final comments.
embedgw/iam.go Adds embedded IAM configuration support. No final comments.
cmd/versitygw/iam.go Passes CLI configuration through. No final comments.
cmd/internal/gwcli/iam.go Adds the CLI flag and environment variable. No final comments.
chart/values.yaml Adds Helm values. No final comments.
chart/templates/iam-deployment.yaml Injects the Helm setting. No final comments.
chart/README.md Documents Helm usage. No final comments.
chart/Chart.yaml Bumps the chart version. No final comments.
Review details

Suppressed comments (2)

iamapi/internal/iamutil/webidentity.go:718

  • The new discovery location is not represented in jwksCacheKey or the singleflight key, which still use only the issuer and thumbprints. If an embedded process restarts the IAM server with the same provider/thumbprints but a changed override (or has two server instances with different overrides), cached/coalesced key material from the old endpoint can be used and the configured endpoint is ignored until the cache expires. Include the resolved discovery target in the cache identity or invalidate the cache when the endpoint policy changes.
	discoveryURL, policy := policy.ResolveDiscovery(issuerURL)
	client := ssrfSafeHTTPClient(thumbprints, policy)

	var doc oidcDiscoveryDoc
	if err := fetchJSON(ctx, client, discoveryURL, &doc); err != nil {

iamapi/server.go:117

  • Splitting at the first = makes overrides unusable for provider URLs that contain = in an otherwise valid path (the existing provider validator accepts paths): https://issuer/tenant=abc=https://cluster/... is parsed with discoveryURL set to abc=https://... and rejected. Since the discovery side is required to start with http:// or https://, locate the separator immediately before that scheme (or define an escaping rule) instead of assuming = cannot occur in the provider URL.
		providerURL, discoveryURL, ok := strings.Cut(pair, "=")
  • Files reviewed: 17/17 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread iamapi/server.go Outdated
`AssumeRoleWithWebIdentity` always fetched a provider's discovery document from `<provider url>/.well-known/openid-configuration`, so an identity provider that issues tokens naming a public issuer while serving its metadata and keys on a cluster-internal path could not be used: reaching it meant relaxing the endpoint checks for every registered provider. `--oidc-discovery-url` moves that one fetch to an operator-named endpoint, which is how keys can be looked up over an optimized private path while the tokens themselves stay verifiable from the public internet against the issuer alone, as the JWT spec requires.

The flag takes `<provider url>=<discovery url>` pairs, can be repeated once per provider, and is also read from `VGW_IAM_OIDC_DISCOVERY_URLS` as a comma-separated list; the Helm chart exposes the same list as `iamServer.oidc.discoveryUrls`. The discovery URL is fetched exactly as written, so it must carry the `/.well-known/openid-configuration` path when the provider serves it there. A malformed pair is rejected at startup rather than at the first assume-role call.

Only the fetch moves. The provider URL is still what a token's `iss` claim is matched against, the fetched document's own `issuer` field must still equal it, and the key set still comes from the `jwks_uri` that document publishes. A configured discovery endpoint is named by the operator at startup rather than by a request, so it and the `jwks_uri` it publishes waive the private-address check for that provider's fetch chain only, without `--oidc-allow-private-endpoints` and its far broader effect on every other provider. Transport rules are unchanged: a plaintext discovery URL still requires `--oidc-allow-insecure-transport`.

Thumbprint auto-fetch follows the override and pins the discovery endpoint's certificate chain, since that is the host every later fetch is verified against.
@niksis02
niksis02 force-pushed the sis/oidc-discovery-urls branch from 2fda0a8 to 1c1272c Compare September 14, 2026 11:13
@benmcclelland
benmcclelland merged commit 9eea7da into main Sep 14, 2026
144 of 145 checks passed
@benmcclelland
benmcclelland deleted the sis/oidc-discovery-urls branch September 14, 2026 18:22
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.

3 participants