mirror of
https://github.com/int128/kubelogin.git
synced 2026-02-14 16:39:51 +00:00
119 lines
3.5 KiB
Go
119 lines
3.5 KiB
Go
package authcode
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/int128/kubelogin/pkg/infrastructure/browser"
|
|
"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/pkce"
|
|
"golang.org/x/sync/errgroup"
|
|
)
|
|
|
|
type BrowserOption struct {
|
|
SkipOpenBrowser bool
|
|
BrowserCommand string
|
|
BindAddress []string
|
|
AuthenticationTimeout time.Duration
|
|
OpenURLAfterAuthentication string
|
|
RedirectURLHostname string
|
|
AuthRequestExtraParams map[string]string
|
|
LocalServerCertFile string
|
|
LocalServerKeyFile string
|
|
}
|
|
|
|
// Browser provides the authentication code flow using the browser.
|
|
type Browser struct {
|
|
Browser browser.Interface
|
|
Logger logger.Interface
|
|
}
|
|
|
|
func (u *Browser) Do(ctx context.Context, o *BrowserOption, oidcClient client.Interface) (*oidc.TokenSet, error) {
|
|
u.Logger.V(1).Infof("starting the authentication code flow using the browser")
|
|
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)
|
|
}
|
|
successHTML := BrowserSuccessHTML
|
|
if o.OpenURLAfterAuthentication != "" {
|
|
successHTML = BrowserRedirectHTML(o.OpenURLAfterAuthentication)
|
|
}
|
|
in := client.GetTokenByAuthCodeInput{
|
|
BindAddress: o.BindAddress,
|
|
State: state,
|
|
Nonce: nonce,
|
|
PKCEParams: pkceParams,
|
|
RedirectURLHostname: o.RedirectURLHostname,
|
|
AuthRequestExtraParams: o.AuthRequestExtraParams,
|
|
LocalServerSuccessHTML: successHTML,
|
|
LocalServerCertFile: o.LocalServerCertFile,
|
|
LocalServerKeyFile: o.LocalServerKeyFile,
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(ctx, o.AuthenticationTimeout)
|
|
defer cancel()
|
|
readyChan := make(chan string, 1)
|
|
var out *oidc.TokenSet
|
|
var eg errgroup.Group
|
|
eg.Go(func() error {
|
|
select {
|
|
case url, ok := <-readyChan:
|
|
if !ok {
|
|
return nil
|
|
}
|
|
u.openURL(ctx, o, url)
|
|
return nil
|
|
case <-ctx.Done():
|
|
return fmt.Errorf("context cancelled while waiting for the local server: %w", ctx.Err())
|
|
}
|
|
})
|
|
eg.Go(func() error {
|
|
defer close(readyChan)
|
|
tokenSet, err := oidcClient.GetTokenByAuthCode(ctx, in, readyChan)
|
|
if err != nil {
|
|
return fmt.Errorf("authorization code flow error: %w", err)
|
|
}
|
|
out = tokenSet
|
|
u.Logger.V(1).Infof("got a token set by the authorization code flow")
|
|
return nil
|
|
})
|
|
if err := eg.Wait(); err != nil {
|
|
return nil, fmt.Errorf("authentication error: %w", err)
|
|
}
|
|
u.Logger.V(1).Infof("finished the authorization code flow via the browser")
|
|
return out, nil
|
|
}
|
|
|
|
func (u *Browser) openURL(ctx context.Context, o *BrowserOption, url string) {
|
|
if o.SkipOpenBrowser {
|
|
u.Logger.Printf("Please visit the following URL in your browser: %s", url)
|
|
return
|
|
}
|
|
|
|
u.Logger.V(1).Infof("opening %s in the browser", url)
|
|
if o.BrowserCommand != "" {
|
|
if err := u.Browser.OpenCommand(ctx, url, o.BrowserCommand); err != nil {
|
|
u.Logger.Printf(`error: could not open the browser: %s
|
|
|
|
Please visit the following URL in your browser manually: %s`, err, url)
|
|
}
|
|
return
|
|
}
|
|
if err := u.Browser.Open(url); err != nil {
|
|
u.Logger.Printf(`error: could not open the browser: %s
|
|
|
|
Please visit the following URL in your browser manually: %s`, err, url)
|
|
}
|
|
}
|