From 98944f741dfed18adbb4c25294970353714e280e Mon Sep 17 00:00:00 2001 From: Tyler Creller Date: Mon, 29 Jan 2024 15:29:24 -0500 Subject: [PATCH] OCM-5759 | feat: Add Device Code Flow (#591) --- cmd/ocm/login/cmd.go | 82 +++++++++++++++++++++++++++++++++++--------- go.mod | 2 +- go.sum | 4 +-- 3 files changed, 68 insertions(+), 20 deletions(-) diff --git a/cmd/ocm/login/cmd.go b/cmd/ocm/login/cmd.go index 2c27ab4f..2f9f6b5c 100644 --- a/cmd/ocm/login/cmd.go +++ b/cmd/ocm/login/cmd.go @@ -17,8 +17,11 @@ limitations under the License. package login import ( + "context" "fmt" + "net/url" "os" + "time" "github.com/openshift-online/ocm-cli/pkg/config" "github.com/openshift-online/ocm-cli/pkg/urls" @@ -48,17 +51,18 @@ var urlAliases = map[string]string{ } var args struct { - tokenURL string - clientID string - clientSecret string - scopes []string - url string - token string - user string - password string - insecure bool - persistent bool - useAuthCode bool + tokenURL string + clientID string + clientSecret string + scopes []string + url string + token string + user string + password string + insecure bool + persistent bool + useAuthCode bool + useDeviceCode bool } var Cmd = &cobra.Command{ @@ -149,14 +153,24 @@ func init() { &args.useAuthCode, "use-auth-code", false, - "Enables OAuth Authorization Code login using PKCE. If this option is provided, "+ - "the user will be taken to Red Hat SSO for authentication. In order to use a different account, "+ - "log out from sso.redhat.com after using the 'ocm logout' command.", + "Login using OAuth Authorization Code. This should be used for most cases where a "+ + "browser is available.", ) flags.MarkHidden("use-auth-code") + flags.BoolVar( + &args.useDeviceCode, + "use-device-code", + false, + "Login using OAuth Device Code. "+ + "This should only be used for remote hosts and containers where browsers are "+ + "not available. Use auth code for all other scenarios.", + ) + flags.MarkHidden("use-device-code") } func run(cmd *cobra.Command, argv []string) error { + ctx := context.Background() + var err error // Check mandatory options: @@ -166,12 +180,34 @@ func run(cmd *cobra.Command, argv []string) error { if args.useAuthCode { fmt.Println("You will now be redirected to Red Hat SSO login") - token, err := authentication.VerifyLogin(oauthClientID) + // Short wait for a less jarring experience + time.Sleep(2 * time.Second) + token, err := authentication.InitiateAuthCode(oauthClientID) + if err != nil { + return fmt.Errorf("an error occurred while retrieving the token : %v", err) + } + args.token = token + args.clientID = oauthClientID + } + + if args.useDeviceCode { + deviceAuthConfig := &authentication.DeviceAuthConfig{ + ClientID: oauthClientID, + } + _, err = deviceAuthConfig.InitiateDeviceAuth(ctx) + if err != nil || deviceAuthConfig == nil { + return fmt.Errorf("an error occurred while initiating device auth: %v", err) + } + deviceAuthResp := deviceAuthConfig.DeviceAuthResponse + fmt.Printf("To login, navigate to %v on another device and enter code %v\n", + deviceAuthResp.VerificationURI, deviceAuthResp.UserCode) + fmt.Printf("Checking status every %v seconds...\n", deviceAuthResp.Interval) + token, err := deviceAuthConfig.PollForTokenExchange(ctx) if err != nil { - return fmt.Errorf("An error occurred while retrieving the token : %v", err) + return fmt.Errorf("an error occurred while polling for token exchange: %v", err) } args.token = token - fmt.Println("Token received successfully") + args.clientID = oauthClientID } // Check that we have some kind of credentials: @@ -291,5 +327,17 @@ func run(cmd *cobra.Command, argv []string) error { return fmt.Errorf("Can't save config file: %v", err) } + if args.useAuthCode || args.useDeviceCode { + ssoURL, err := url.Parse(cfg.TokenURL) + if err != nil { + return fmt.Errorf("can't parse token url '%s': %v", args.tokenURL, err) + } + ssoHost := ssoURL.Scheme + "://" + ssoURL.Hostname() + + fmt.Println("Login successful") + fmt.Printf("To switch accounts, logout from %s and run `ocm logout` "+ + "before attempting to login again", ssoHost) + } + return nil } diff --git a/go.mod b/go.mod index 13175f82..2e60a6d3 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/nwidger/jsoncolor v0.3.2 github.com/onsi/ginkgo/v2 v2.11.0 github.com/onsi/gomega v1.27.8 - github.com/openshift-online/ocm-sdk-go v0.1.393 + github.com/openshift-online/ocm-sdk-go v0.1.395 github.com/openshift/rosa v1.2.24 github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 github.com/spf13/cobra v1.7.0 diff --git a/go.sum b/go.sum index 0b24a3e9..1e584dd1 100644 --- a/go.sum +++ b/go.sum @@ -309,8 +309,8 @@ github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= -github.com/openshift-online/ocm-sdk-go v0.1.393 h1:GjjgK70yTV5hBOgdH4x+2PoXn4W+5x+o7xCwm7fOGHw= -github.com/openshift-online/ocm-sdk-go v0.1.393/go.mod h1:tke8vKcE7eHKyRbkJv6qo4ljo919zhx04uyQTcgF5cQ= +github.com/openshift-online/ocm-sdk-go v0.1.395 h1:Lt4IJLHy+ArpCprZQqh2G8ifQr3wOP1l7yziU/5l7+Q= +github.com/openshift-online/ocm-sdk-go v0.1.395/go.mod h1:tke8vKcE7eHKyRbkJv6qo4ljo919zhx04uyQTcgF5cQ= github.com/openshift/rosa v1.2.24 h1:vv0yYnWHx6CCPEAau/0rS54P2ksaf+uWXb1TQPWxiYE= github.com/openshift/rosa v1.2.24/go.mod h1:MVXB27O3PF8WoOic23I03mmq6/9kVxpFx6FKyLMCyrQ= github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=