mirror of
https://github.com/int128/kubelogin.git
synced 2026-05-09 01:16:35 +00:00
The token cache key computation did not include the AuthRequestExtraParams values from the --oidc-auth-request-extra-params flag. This caused tokens with different extra parameters (e.g., different audience values) to incorrectly share the same cache entry. Changes: - Add AuthRequestExtraParams field to tokencache.Key struct - Add AuthRequestExtraParams() method to GrantOptionSet to extract extra params from whichever grant option is set - Update get_token.go to include extra params in cache key - Add comprehensive tests for cache key differentiation Fixes #1496
151 lines
5.2 KiB
Go
151 lines
5.2 KiB
Go
package authentication
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/google/wire"
|
|
"github.com/int128/kubelogin/pkg/infrastructure/logger"
|
|
"github.com/int128/kubelogin/pkg/oidc"
|
|
"github.com/int128/kubelogin/pkg/oidc/client"
|
|
"github.com/int128/kubelogin/pkg/tlsclientconfig"
|
|
"github.com/int128/kubelogin/pkg/usecases/authentication/authcode"
|
|
"github.com/int128/kubelogin/pkg/usecases/authentication/clientcredentials"
|
|
"github.com/int128/kubelogin/pkg/usecases/authentication/devicecode"
|
|
"github.com/int128/kubelogin/pkg/usecases/authentication/ropc"
|
|
)
|
|
|
|
// Set provides the use-case of Authentication.
|
|
var Set = wire.NewSet(
|
|
wire.Struct(new(Authentication), "*"),
|
|
wire.Bind(new(Interface), new(*Authentication)),
|
|
wire.Struct(new(authcode.Browser), "*"),
|
|
wire.Struct(new(authcode.Keyboard), "*"),
|
|
wire.Struct(new(ropc.ROPC), "*"),
|
|
wire.Struct(new(devicecode.DeviceCode), "*"),
|
|
wire.Struct(new(clientcredentials.ClientCredentials), "*"),
|
|
)
|
|
|
|
type Interface interface {
|
|
Do(ctx context.Context, in Input) (*Output, error)
|
|
}
|
|
|
|
// Input represents an input DTO of the Authentication use-case.
|
|
type Input struct {
|
|
Provider oidc.Provider
|
|
GrantOptionSet GrantOptionSet
|
|
CachedTokenSet *oidc.TokenSet // optional
|
|
TLSClientConfig tlsclientconfig.Config
|
|
}
|
|
|
|
type GrantOptionSet struct {
|
|
AuthCodeBrowserOption *authcode.BrowserOption
|
|
AuthCodeKeyboardOption *authcode.KeyboardOption
|
|
ROPCOption *ropc.Option
|
|
DeviceCodeOption *devicecode.Option
|
|
ClientCredentialsOption *client.GetTokenByClientCredentialsInput
|
|
}
|
|
|
|
// AuthRequestExtraParams returns the extra parameters for the auth request
|
|
// from whichever grant option is set.
|
|
func (g GrantOptionSet) AuthRequestExtraParams() map[string]string {
|
|
if g.AuthCodeBrowserOption != nil {
|
|
return g.AuthCodeBrowserOption.AuthRequestExtraParams
|
|
}
|
|
if g.AuthCodeKeyboardOption != nil {
|
|
return g.AuthCodeKeyboardOption.AuthRequestExtraParams
|
|
}
|
|
if g.ClientCredentialsOption != nil && g.ClientCredentialsOption.EndpointParams != nil {
|
|
// Convert map[string][]string back to map[string]string
|
|
params := make(map[string]string, len(g.ClientCredentialsOption.EndpointParams))
|
|
for k, v := range g.ClientCredentialsOption.EndpointParams {
|
|
if len(v) > 0 {
|
|
params[k] = v[0]
|
|
}
|
|
}
|
|
return params
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Output represents an output DTO of the Authentication use-case.
|
|
type Output struct {
|
|
TokenSet oidc.TokenSet
|
|
}
|
|
|
|
// Authentication provides the internal use-case of authentication.
|
|
//
|
|
// If the IDToken is not set, it performs the authentication flow.
|
|
// If the IDToken is valid, it does nothing.
|
|
// If the IDToken has expired and the RefreshToken is set, it refreshes the token.
|
|
// If the RefreshToken has expired, it performs the authentication flow.
|
|
//
|
|
// The authentication flow is determined as:
|
|
//
|
|
// If the Username is not set, it performs the authorization code flow.
|
|
// Otherwise, it performs the resource owner password credentials flow.
|
|
// If the Password is not set, it asks a password by the prompt.
|
|
type Authentication struct {
|
|
ClientFactory client.FactoryInterface
|
|
Logger logger.Interface
|
|
AuthCodeBrowser *authcode.Browser
|
|
AuthCodeKeyboard *authcode.Keyboard
|
|
ROPC *ropc.ROPC
|
|
DeviceCode *devicecode.DeviceCode
|
|
ClientCredentials *clientcredentials.ClientCredentials
|
|
}
|
|
|
|
func (u *Authentication) Do(ctx context.Context, in Input) (*Output, error) {
|
|
u.Logger.V(1).Infof("initializing an OpenID Connect client")
|
|
oidcClient, err := u.ClientFactory.New(ctx, in.Provider, in.TLSClientConfig)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("oidc error: %w", err)
|
|
}
|
|
|
|
if in.CachedTokenSet != nil && in.CachedTokenSet.RefreshToken != "" {
|
|
u.Logger.V(1).Infof("refreshing the token")
|
|
tokenSet, err := oidcClient.Refresh(ctx, in.CachedTokenSet.RefreshToken)
|
|
if err == nil {
|
|
return &Output{TokenSet: *tokenSet}, nil
|
|
}
|
|
u.Logger.V(1).Infof("could not refresh the token: %s", err)
|
|
}
|
|
|
|
if in.GrantOptionSet.AuthCodeBrowserOption != nil {
|
|
tokenSet, err := u.AuthCodeBrowser.Do(ctx, in.GrantOptionSet.AuthCodeBrowserOption, oidcClient)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("authcode-browser error: %w", err)
|
|
}
|
|
return &Output{TokenSet: *tokenSet}, nil
|
|
}
|
|
if in.GrantOptionSet.AuthCodeKeyboardOption != nil {
|
|
tokenSet, err := u.AuthCodeKeyboard.Do(ctx, in.GrantOptionSet.AuthCodeKeyboardOption, oidcClient)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("authcode-keyboard error: %w", err)
|
|
}
|
|
return &Output{TokenSet: *tokenSet}, nil
|
|
}
|
|
if in.GrantOptionSet.ROPCOption != nil {
|
|
tokenSet, err := u.ROPC.Do(ctx, in.GrantOptionSet.ROPCOption, oidcClient)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("ropc error: %w", err)
|
|
}
|
|
return &Output{TokenSet: *tokenSet}, nil
|
|
}
|
|
if in.GrantOptionSet.DeviceCodeOption != nil {
|
|
tokenSet, err := u.DeviceCode.Do(ctx, in.GrantOptionSet.DeviceCodeOption, oidcClient)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("device-code error: %w", err)
|
|
}
|
|
return &Output{TokenSet: *tokenSet}, nil
|
|
}
|
|
if in.GrantOptionSet.ClientCredentialsOption != nil {
|
|
tokenSet, err := u.ClientCredentials.Do(ctx, in.GrantOptionSet.ClientCredentialsOption, oidcClient)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("client-credentials error: %w", err)
|
|
}
|
|
return &Output{TokenSet: *tokenSet}, nil
|
|
}
|
|
return nil, fmt.Errorf("any authorization grant must be set")
|
|
}
|