feat(runs): serve external OAuth2 authorization-server metadata#7507
Conversation
Implement GetOAuth2Metadata in the runs AuthMetadataService by proxying an external authorization server's (e.g. Okta) metadata document, and mount it at the RFC 8414 /.well-known/oauth-authorization-server path. Adds runs config (authMetadata.externalAuthServerBaseUrl, ...) and tests. External-fetch logic adapted from #6998. Signed-off-by: Kevin Su <pingsutw@gmail.com> Signed-off-by: Kevin Su <pingsutw@apache.org>
Port flyteadmin's OAuth2MetadataProvider.GetPublicClientConfig (auth/authzserver/metadata_provider.go): serve the public CLI/SDK OAuth2 client settings (clientId, redirectUri, scopes, audience, authorizationMetadataKey) from runs.authMetadata config instead of the previous stub that only returned dataplaneDomain. Empty authorizationMetadataKey defaults to the standard "authorization" header so upstream JWT validators (e.g. ALB) see the token. Signed-off-by: Kevin Su <pingsutw@gmail.com> Signed-off-by: Kevin Su <pingsutw@apache.org>
There was a problem hiding this comment.
Pull request overview
This PR enables OAuth2/OIDC discovery for the v2 runs service by implementing flyteidl2.auth.AuthMetadataService.GetOAuth2Metadata and exposing the RFC 8414 well-known endpoint (/.well-known/oauth-authorization-server) that proxies an externally configured authorization server’s metadata document.
Changes:
- Implements external OAuth2 authorization-server metadata proxying with retry and protojson parsing (discarding unknown fields).
- Adds an HTTP handler for
/.well-known/oauth-authorization-serverserving proto3 JSON. - Introduces runs config for auth metadata proxying + public client settings, and adds unit tests covering the new behavior.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| runs/setup.go | Wires new AuthMetadataService config and mounts the RFC 8414 well-known HTTP route. |
| runs/service/auth_metadata_service.go | Implements GetOAuth2Metadata proxying + HTTP handler and supporting helpers. |
| runs/service/auth_metadata_service_test.go | Adds unit tests for configured/unconfigured behavior and JSON shape handling. |
| runs/config/config.go | Adds runs.authMetadata configuration (external metadata proxy + public client settings). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Kevin Su <pingsutw@gmail.com>
…ew suggestion Signed-off-by: Kevin Su <pingsutw@apache.org>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Kevin Su <pingsutw@gmail.com>
…tadataService Drop the service-local ExternalAuthServerConfig/PublicClientConfig mirror structs and pass cfg.AuthMetadata wholesale, removing the field-by-field copy in setup.go. Signed-off-by: Kevin Su <pingsutw@apache.org>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Kevin Su <pingsutw@gmail.com>
| retryDelay = time.Second | ||
| } | ||
|
|
||
| client := &http.Client{Timeout: 10 * time.Second} |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Kevin Su <pingsutw@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Kevin Su <pingsutw@gmail.com>
- Reuse a single http.Client (10s timeout) instead of per-request construction - Bound the upstream metadata read to 1 MiB - Cache successfully fetched metadata for 5 minutes (the endpoint is public and the document is effectively static; avoids an outbound fetch with retries per discovery call) - Classify non-retryable upstream 4xx as Internal and propagate pre-classified connect codes instead of blanket Unavailable - Fix discovery-clients wording in setup.go Signed-off-by: Kevin Su <pingsutw@gmail.com> Signed-off-by: Kevin Su <pingsutw@apache.org>
|
Triage of the Copilot review comments as of Fixed in 84ec9d4
|
go generate ./runs/config was failing on types the pflags generator does not support, so config_flags.go had gone stale (it also predated the triggerScheduler fields). Unblock generation and regenerate: - RetryDelay: time.Duration -> flytestdlib config.Duration (idiomatic, keeps the flag) - Domains, triggerScheduler.resyncInterval, triggerScheduler.executionQps: pflag:"-" (slice-of-struct / raw duration / float64 are unsupported by the generator; config-file only) Generated flags now include runs.authMetadata.* (incl. flyteClient.*) and the previously missing triggerScheduler.* entries. Signed-off-by: Kevin Su <pingsutw@gmail.com> Signed-off-by: Kevin Su <pingsutw@apache.org>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Kevin Su <pingsutw@gmail.com>
| ) (*connect.Response[auth.GetPublicClientConfigResponse], error) { | ||
| authMetadataKey := s.cfg.AuthorizationMetadataKey | ||
| if authMetadataKey == "" { | ||
| authMetadataKey = "authorization" |
There was a problem hiding this comment.
Move to const.
Shouldn't it be Authorization (capitalized)?
There was a problem hiding this comment.
| // Serve from cache while fresh; only successful fetches are cached. | ||
| s.mu.Lock() | ||
| if s.cachedMetadata != nil && time.Now().Before(s.cacheExpiry) { | ||
| cached := proto.Clone(s.cachedMetadata).(*auth.GetOAuth2MetadataResponse) | ||
| s.mu.Unlock() | ||
| return connect.NewResponse(cached), nil | ||
| } | ||
| s.mu.Unlock() |
There was a problem hiding this comment.
Make it an RWLock instead so multiple readers don't block
| // so OAuth2/OIDC discovery clients (flyte-sdk) can find it. When | ||
| // runs.authMetadata.externalAuthServerBaseUrl is set, this proxies the | ||
| // external IdP's (e.g. Okta) metadata document. | ||
| sc.Mux.Handle("/.well-known/oauth-authorization-server", service.OAuth2MetadataHTTPHandler(authMetadataSvc)) |
There was a problem hiding this comment.
Do we also need to serve /.well-known/openid-configuration? maybe not
There was a problem hiding this comment.
I don't use that so far, I'll add it later if needed
| // GetPublicClientConfig returns the public (CLI/SDK) OAuth2 client settings. | ||
| func (s *AuthMetadataService) GetPublicClientConfig( | ||
| ctx context.Context, | ||
| req *connect.Request[auth.GetPublicClientConfigRequest], |
There was a problem hiding this comment.
nit: Can we fix the "ctx" and "req" variable name to "_" as they are unused?
| switch connectErr.Code() { | ||
| case connect.CodeUnimplemented: | ||
| return http.StatusNotImplemented | ||
| case connect.CodeInvalidArgument: |
There was a problem hiding this comment.
nit: Should we remove this case as we don't return connect.CodeInvalidArgument error in GetOAuth2Metadata function?
There was a problem hiding this comment.
I'll keep it. we may return that in the future
Tracking issue
Related to #6998
Why are the changes needed?
The v2 runs service exposes
flyteidl2.auth.AuthMetadataService, butGetOAuth2Metadatais an unimplemented stub (returnsUnimplemented/501), andthere is no
/.well-known/oauth-authorization-serverHTTP route. As a result,OAuth2/OIDC discovery clients (flyte-sdk) and upstream proxies cannot
learn which authorization server to use from a v2 deployment.
This is needed when a deployment fronts its API with an external identity
provider (e.g. Okta) — for example when an ALB validates Okta-issued JWTs in
front of the API. Clients that discover auth at the deployment host must be
pointed at the external IdP so they obtain tokens the upstream actually accepts.
What changes were proposed in this pull request?
Implement OAuth2 authorization-server metadata in the runs service by proxying
an external authorization server's metadata document:
runs/service/auth_metadata_service.go— implementGetOAuth2Metadata:when an external auth server is configured, fetch its
.well-known/oauth-authorization-server(with retry) and return it. Unknownfields are discarded (
protojsonwithDiscardUnknown) so real servers likeOkta — which return many extra metadata fields — parse cleanly. When not
configured, it returns
Unimplemented. Also addsOAuth2MetadataHTTPHandler,which serves the document at the RFC 8414 well-known path as proto3 JSON.
runs/setup.go— pass the new config intoNewAuthMetadataServiceandmount
GET /.well-known/oauth-authorization-serveron the mux.runs/config/config.go— addAuthMetadataConfig(
externalAuthServerBaseUrl,externalMetadataUrl,retryAttempts,retryDelay) under therunsconfig section.The external-fetch/unmarshal/retry logic is adapted from #6998; this PR ports
just the metadata-serving slice (no token issuance / cookie / middleware stack).
How was this patch tested?
Unit tests in
runs/service/auth_metadata_service_test.go:Unimplemented(RPC) /501(HTTP)fields (Okta-shaped) via
DiscardUnknown5xx→Unavailable200with camelCase proto3 JSONgo build ./runs/...,go vet, andgofmtclean.Labels
added
Check all the applicable boxes