mirror of
https://github.com/int128/kubelogin.git
synced 2026-02-14 16:39:51 +00:00
* Lock token cache file in authentication * Fix tests * make generate * Lock before FindByKey * Fix test
127 lines
4.8 KiB
Go
127 lines
4.8 KiB
Go
package standalone
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/google/wire"
|
|
"github.com/int128/kubelogin/pkg/infrastructure/clock"
|
|
"github.com/int128/kubelogin/pkg/infrastructure/logger"
|
|
"github.com/int128/kubelogin/pkg/kubeconfig"
|
|
"github.com/int128/kubelogin/pkg/kubeconfig/loader"
|
|
"github.com/int128/kubelogin/pkg/kubeconfig/writer"
|
|
"github.com/int128/kubelogin/pkg/oidc"
|
|
"github.com/int128/kubelogin/pkg/tlsclientconfig"
|
|
"github.com/int128/kubelogin/pkg/usecases/authentication"
|
|
)
|
|
|
|
// Set provides the use-case.
|
|
var Set = wire.NewSet(
|
|
wire.Struct(new(Standalone), "*"),
|
|
wire.Bind(new(Interface), new(*Standalone)),
|
|
)
|
|
|
|
type Interface interface {
|
|
Do(ctx context.Context, in Input) error
|
|
}
|
|
|
|
// Input represents an input DTO of the use-case.
|
|
type Input struct {
|
|
KubeconfigFilename string // Default to the environment variable or global config as kubectl
|
|
KubeconfigContext kubeconfig.ContextName // Default to the current context but ignored if KubeconfigUser is set
|
|
KubeconfigUser kubeconfig.UserName // Default to the user of the context
|
|
GrantOptionSet authentication.GrantOptionSet
|
|
TLSClientConfig tlsclientconfig.Config
|
|
}
|
|
|
|
const oidcConfigErrorMessage = `No configuration found.
|
|
You need to set up the OIDC provider, role binding, Kubernetes API server and kubeconfig.
|
|
To show the setup instruction:
|
|
|
|
kubectl oidc-login setup
|
|
|
|
See https://github.com/int128/kubelogin for more.
|
|
`
|
|
|
|
// Standalone provides the use case of explicit login.
|
|
//
|
|
// If the current auth provider is not oidc, show the error.
|
|
// If the kubeconfig has a valid token, do nothing.
|
|
// Otherwise, update the kubeconfig.
|
|
type Standalone struct {
|
|
Authentication authentication.Interface
|
|
KubeconfigLoader loader.Interface
|
|
KubeconfigWriter writer.Interface
|
|
Logger logger.Interface
|
|
Clock clock.Interface
|
|
}
|
|
|
|
func (u *Standalone) Do(ctx context.Context, in Input) error {
|
|
u.Logger.V(1).Infof("WARNING: log may contain your secrets such as token or password")
|
|
|
|
authProvider, err := u.KubeconfigLoader.GetCurrentAuthProvider(in.KubeconfigFilename, in.KubeconfigContext, in.KubeconfigUser)
|
|
if err != nil {
|
|
u.Logger.Printf(oidcConfigErrorMessage)
|
|
return fmt.Errorf("could not find the current authentication provider: %w", err)
|
|
}
|
|
u.Logger.V(1).Infof("using the authentication provider of the user %s", authProvider.UserName)
|
|
u.Logger.V(1).Infof("a token will be written to %s", authProvider.LocationOfOrigin)
|
|
if authProvider.IDPCertificateAuthority != "" {
|
|
u.Logger.V(1).Infof("using the certificate %s", authProvider.IDPCertificateAuthority)
|
|
in.TLSClientConfig.CACertFilename = append(in.TLSClientConfig.CACertFilename, authProvider.IDPCertificateAuthority)
|
|
}
|
|
if authProvider.IDPCertificateAuthorityData != "" {
|
|
u.Logger.V(1).Infof("using the certificate in %s", authProvider.LocationOfOrigin)
|
|
in.TLSClientConfig.CACertData = append(in.TLSClientConfig.CACertData, authProvider.IDPCertificateAuthorityData)
|
|
}
|
|
var cachedTokenSet *oidc.TokenSet
|
|
if authProvider.IDToken != "" {
|
|
cachedTokenSet = &oidc.TokenSet{
|
|
IDToken: authProvider.IDToken,
|
|
RefreshToken: authProvider.RefreshToken,
|
|
}
|
|
u.Logger.V(1).Infof("checking expiration of the existing token")
|
|
// Skip verification of the token to reduce time of a discovery request.
|
|
// Here it trusts the signature and claims and checks only expiration,
|
|
// because the token has been verified before caching.
|
|
claims, err := cachedTokenSet.DecodeWithoutVerify()
|
|
if err != nil {
|
|
return fmt.Errorf("invalid token cache (you may need to remove): %w", err)
|
|
}
|
|
if !claims.IsExpired(u.Clock) {
|
|
u.Logger.V(1).Infof("you already have a valid token until %s", claims.Expiry)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
authenticationInput := authentication.Input{
|
|
Provider: oidc.Provider{
|
|
IssuerURL: authProvider.IDPIssuerURL,
|
|
ClientID: authProvider.ClientID,
|
|
ClientSecret: authProvider.ClientSecret,
|
|
ExtraScopes: authProvider.ExtraScopes,
|
|
},
|
|
GrantOptionSet: in.GrantOptionSet,
|
|
CachedTokenSet: cachedTokenSet,
|
|
TLSClientConfig: in.TLSClientConfig,
|
|
}
|
|
authenticationOutput, err := u.Authentication.Do(ctx, authenticationInput)
|
|
if err != nil {
|
|
return fmt.Errorf("authentication error: %w", err)
|
|
}
|
|
|
|
idTokenClaims, err := authenticationOutput.TokenSet.DecodeWithoutVerify()
|
|
if err != nil {
|
|
return fmt.Errorf("you got an invalid token: %w", err)
|
|
}
|
|
u.Logger.V(1).Infof("you got a token: %s", idTokenClaims.Pretty)
|
|
u.Logger.Printf("You got a valid token until %s", idTokenClaims.Expiry)
|
|
authProvider.IDToken = authenticationOutput.TokenSet.IDToken
|
|
authProvider.RefreshToken = authenticationOutput.TokenSet.RefreshToken
|
|
u.Logger.V(1).Infof("writing the ID token and refresh token to %s", authProvider.LocationOfOrigin)
|
|
if err := u.KubeconfigWriter.UpdateAuthProvider(*authProvider); err != nil {
|
|
return fmt.Errorf("could not update the kubeconfig: %w", err)
|
|
}
|
|
return nil
|
|
}
|