package authcode import ( "context" "fmt" "github.com/int128/kubelogin/pkg/infrastructure/logger" "github.com/int128/kubelogin/pkg/infrastructure/reader" "github.com/int128/kubelogin/pkg/oidc" "github.com/int128/kubelogin/pkg/oidc/client" "github.com/int128/kubelogin/pkg/pkce" ) const keyboardPrompt = "Enter code: " type KeyboardOption struct { AuthRequestExtraParams map[string]string } // Keyboard provides the authorization code flow with keyboard interactive. type Keyboard struct { Reader reader.Interface Logger logger.Interface } func (u *Keyboard) Do(ctx context.Context, o *KeyboardOption, oidcClient client.Interface) (*oidc.TokenSet, error) { u.Logger.V(1).Infof("starting the authorization code flow with keyboard interactive") state, err := oidc.NewState() if err != nil { return nil, fmt.Errorf("could not generate a state: %w", err) } nonce, err := oidc.NewNonce() if err != nil { return nil, fmt.Errorf("could not generate a nonce: %w", err) } pkceParams, err := pkce.New(oidcClient.NegotiatedPKCEMethod()) if err != nil { return nil, fmt.Errorf("could not generate the PKCE parameters: %w", err) } authCodeURL := oidcClient.GetAuthCodeURL(client.AuthCodeURLInput{ State: state, Nonce: nonce, PKCEParams: pkceParams, AuthRequestExtraParams: o.AuthRequestExtraParams, }) u.Logger.Printf("Please visit the following URL in your browser: %s", authCodeURL) code, err := u.Reader.ReadString(keyboardPrompt) if err != nil { return nil, fmt.Errorf("could not read an authorization code: %w", err) } u.Logger.V(1).Infof("exchanging the code and token") tokenSet, err := oidcClient.ExchangeAuthCode(ctx, client.ExchangeAuthCodeInput{ Code: code, PKCEParams: pkceParams, Nonce: nonce, }) if err != nil { return nil, fmt.Errorf("could not exchange the authorization code: %w", err) } u.Logger.V(1).Infof("finished the authorization code flow with keyboard interactive") return tokenSet, nil }