router/pkg/authentication/oidc_discovery_client.go (40 lines of code) (raw):
package authentication
import (
"encoding/json"
"net/http"
"strings"
)
// oidcDiscoveryPath is the path to the OIDC discovery endpoint.
// Per spec, this path must be /.well-known/openid-configuration.
// See https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig.
const oidcDiscoveryPath = "/.well-known/openid-configuration"
var _ http.RoundTripper = &oidcDiscoveryClient{}
type oidcConfiguration struct {
JwksURI string `json:"jwks_uri"`
}
// oidcDiscoveryClient is a http.RoundTripper that fetches the JWKS from the OIDC discovery endpoint.
// If the endpoint is not an OIDC discovery endpoint, it delegates the request to the underlying http.Client.
type oidcDiscoveryClient struct {
httpClient *http.Client
}
func newOIDCDiscoveryClient(httpClient *http.Client) *http.Client {
if httpClient == nil {
httpClient = http.DefaultClient
}
return &http.Client{Transport: &oidcDiscoveryClient{httpClient}}
}
// RoundTrip implements http.RoundTripper.
// If the request is for the OIDC discovery endpoint, it fetches the JWKS from the JWKS URI.
// Otherwise, it delegates the request to the underlying http.Client.
func (c *oidcDiscoveryClient) RoundTrip(req *http.Request) (*http.Response, error) {
if !strings.HasSuffix(req.URL.Path, oidcDiscoveryPath) {
return c.httpClient.Do(req)
}
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
oidcConfig := new(oidcConfiguration)
if err := json.NewDecoder(resp.Body).Decode(oidcConfig); err != nil {
return nil, err
}
disReq, err := http.NewRequestWithContext(req.Context(), http.MethodGet, oidcConfig.JwksURI, nil)
if err != nil {
return nil, err
}
disReq.Header = req.Header
return c.httpClient.Do(disReq)
}