Skip to content

Commit

Permalink
add login flow using a browser
Browse files Browse the repository at this point in the history
This change adds a new flag called `--web` which launches the system
browser with the authorization endpoint as the target. The user can then
authenticate and is redirected back to a server which is listening on
the loopback address on a random port. The callback receives the
authorization code which it can exchange for a token and configure the
client.
  • Loading branch information
liouk committed Nov 15, 2023
1 parent 24629ee commit d91f4f2
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 6 deletions.
18 changes: 16 additions & 2 deletions pkg/cli/login/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ var (
The information required to login -- like username and password, a session token, or
the server details -- can be provided through flags. If not provided, the command will
prompt for user input as needed.
prompt for user input as needed. It is also possible to login through a web browser by
providing the respective flag.
`)

loginExample = templates.Examples(`
Expand All @@ -43,6 +44,9 @@ var (
# Log in to the given server with the given credentials (will not prompt interactively)
oc login localhost:8443 --username=myuser --password=mypass
# Log in to the given server through a browser
oc login --web --callback-port 8280 localhost:8443
`)
)

Expand Down Expand Up @@ -83,6 +87,8 @@ func NewCmdLogin(f kcmdutil.Factory, streams genericclioptions.IOStreams) *cobra
cmds.Flags().StringVarP(&o.Username, "username", "u", o.Username, "Username for server")
cmds.Flags().StringVarP(&o.Password, "password", "p", o.Password, "Password for server")

cmds.Flags().BoolVarP(&o.WebLogin, "web", "w", o.WebLogin, "Login with web browser")
cmds.Flags().Int32VarP(&o.CallbackPort, "callback-port", "c", o.CallbackPort, "Port for the callback server when using --web. Defaults to a random open port")
return cmds
}

Expand Down Expand Up @@ -167,10 +173,18 @@ func (o LoginOptions) Validate(cmd *cobra.Command, serverFlag string, args []str
return errors.New("Must have a config file already created")
}

if o.WebLogin && (o.Username != "" || o.Password != "" || o.Token != "") {
return errors.New("--web cannot be used along with --username, --password or --token")
}

if o.CallbackPort != 0 && !o.WebLogin {
return errors.New("--callback-port can only be specified along with --web")
}

return nil
}

// RunLogin contains all the necessary functionality for the OpenShift cli login command
// Run contains all the necessary functionality for the OpenShift cli login command
func (o LoginOptions) Run() error {
if err := o.GatherInfo(); err != nil {
return err
Expand Down
23 changes: 19 additions & 4 deletions pkg/cli/login/loginoptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"io"
"net"
"net/url"
"os"
"path/filepath"
"time"
Expand All @@ -34,6 +35,7 @@ import (
loginutil "github.com/openshift/oc/pkg/helpers/project"
"github.com/openshift/oc/pkg/helpers/term"
"github.com/openshift/oc/pkg/version"
"github.com/pkg/browser"
)

const defaultClusterURL = "https://localhost:8443"
Expand All @@ -52,9 +54,11 @@ type LoginOptions struct {
InsecureTLS bool

// flags and printing helpers
Username string
Password string
Project string
Username string
Password string
Project string
WebLogin bool
CallbackPort int32

// infra
StartingKubeConfig *kclientcmdapi.Config
Expand Down Expand Up @@ -257,10 +261,21 @@ func (o *LoginOptions) gatherAuthInfo() error {
clientConfig.CertFile = o.CertFile
clientConfig.KeyFile = o.KeyFile

token, err := tokenrequest.RequestTokenWithChallengeHandlers(o.Config, o.getAuthChallengeHandler())
var token string
if o.WebLogin {
loginURLHandler := func(u *url.URL) error {
loginURL := u.String()
fmt.Fprintf(o.Out, "Opening login URL in the default browser: %s\n", loginURL)
return browser.OpenURL(loginURL)
}
token, err = tokenrequest.RequestTokenWithLocalCallback(o.Config, loginURLHandler, int(o.CallbackPort))
} else {
token, err = tokenrequest.RequestTokenWithChallengeHandlers(o.Config, o.getAuthChallengeHandler())
}
if err != nil {
return err
}

clientConfig.BearerToken = token

me, err := project.WhoAmI(clientConfig)
Expand Down

0 comments on commit d91f4f2

Please sign in to comment.