mirror of
https://github.com/int128/kubelogin.git
synced 2026-08-23 21:06:15 +00:00
* Add --oidc-pkce-method and improve docs * Fix lint * Refactor * Refactor
118 lines
3.6 KiB
Go
118 lines
3.6 KiB
Go
// Package client provides a client of OpenID Connect.
|
|
package client
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"slices"
|
|
|
|
gooidc "github.com/coreos/go-oidc/v3/oidc"
|
|
"github.com/google/wire"
|
|
"github.com/int128/kubelogin/pkg/infrastructure/clock"
|
|
"github.com/int128/kubelogin/pkg/infrastructure/logger"
|
|
"github.com/int128/kubelogin/pkg/oidc"
|
|
"github.com/int128/kubelogin/pkg/oidc/client/logging"
|
|
"github.com/int128/kubelogin/pkg/pkce"
|
|
"github.com/int128/kubelogin/pkg/tlsclientconfig"
|
|
"github.com/int128/kubelogin/pkg/tlsclientconfig/loader"
|
|
"golang.org/x/oauth2"
|
|
)
|
|
|
|
var Set = wire.NewSet(
|
|
wire.Struct(new(Factory), "*"),
|
|
wire.Bind(new(FactoryInterface), new(*Factory)),
|
|
)
|
|
|
|
type FactoryInterface interface {
|
|
New(ctx context.Context, prov oidc.Provider, tlsClientConfig tlsclientconfig.Config) (Interface, error)
|
|
}
|
|
|
|
type Factory struct {
|
|
Loader loader.Loader
|
|
Clock clock.Interface
|
|
Logger logger.Interface
|
|
}
|
|
|
|
// New returns an instance of infrastructure.Interface with the given configuration.
|
|
func (f *Factory) New(ctx context.Context, prov oidc.Provider, tlsClientConfig tlsclientconfig.Config) (Interface, error) {
|
|
rawTLSClientConfig, err := f.Loader.Load(tlsClientConfig)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not load the TLS client config: %w", err)
|
|
}
|
|
baseTransport := &http.Transport{
|
|
TLSClientConfig: rawTLSClientConfig,
|
|
Proxy: http.ProxyFromEnvironment,
|
|
}
|
|
loggingTransport := &logging.Transport{
|
|
Base: baseTransport,
|
|
Logger: f.Logger,
|
|
}
|
|
httpClient := &http.Client{
|
|
Transport: loggingTransport,
|
|
}
|
|
|
|
ctx = context.WithValue(ctx, oauth2.HTTPClient, httpClient)
|
|
provider, err := gooidc.NewProvider(ctx, prov.IssuerURL)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("oidc discovery error: %w", err)
|
|
}
|
|
supportedPKCEMethods, err := extractSupportedPKCEMethods(provider)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not determine supported PKCE methods: %w", err)
|
|
}
|
|
deviceAuthorizationEndpoint, err := extractDeviceAuthorizationEndpoint(provider)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not determine device authorization endpoint: %w", err)
|
|
}
|
|
return &client{
|
|
httpClient: httpClient,
|
|
provider: provider,
|
|
oauth2Config: oauth2.Config{
|
|
Endpoint: provider.Endpoint(),
|
|
ClientID: prov.ClientID,
|
|
ClientSecret: prov.ClientSecret,
|
|
Scopes: append(prov.ExtraScopes, gooidc.ScopeOpenID),
|
|
},
|
|
clock: f.Clock,
|
|
logger: f.Logger,
|
|
negotiatedPKCEMethod: determinePKCEMethod(supportedPKCEMethods, prov.PKCEMethod),
|
|
deviceAuthorizationEndpoint: deviceAuthorizationEndpoint,
|
|
useAccessToken: prov.UseAccessToken,
|
|
}, nil
|
|
}
|
|
|
|
func determinePKCEMethod(supportedMethods []string, preferredMethod oidc.PKCEMethod) pkce.Method {
|
|
switch preferredMethod {
|
|
case oidc.PKCEMethodNo:
|
|
return pkce.NoMethod
|
|
case oidc.PKCEMethodS256:
|
|
return pkce.MethodS256
|
|
default:
|
|
if slices.Contains(supportedMethods, "S256") {
|
|
return pkce.MethodS256
|
|
}
|
|
return pkce.NoMethod
|
|
}
|
|
}
|
|
|
|
func extractSupportedPKCEMethods(provider *gooidc.Provider) ([]string, error) {
|
|
var claims struct {
|
|
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported"`
|
|
}
|
|
if err := provider.Claims(&claims); err != nil {
|
|
return nil, fmt.Errorf("invalid discovery document: %w", err)
|
|
}
|
|
return claims.CodeChallengeMethodsSupported, nil
|
|
}
|
|
|
|
func extractDeviceAuthorizationEndpoint(provider *gooidc.Provider) (string, error) {
|
|
var claims struct {
|
|
DeviceAuthorizationEndpoint string `json:"device_authorization_endpoint"`
|
|
}
|
|
if err := provider.Claims(&claims); err != nil {
|
|
return "", fmt.Errorf("invalid discovery document: %w", err)
|
|
}
|
|
return claims.DeviceAuthorizationEndpoint, nil
|
|
}
|