From 059127224cafb946b02d5fb2ce85bb528b956486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20Gustav=20Str=C3=A5b=C3=B8?= Date: Fri, 12 Apr 2024 12:41:21 +0200 Subject: [PATCH 1/5] replace device code login with interactive login --- pkg/client/auth/client.go | 5 +---- pkg/client/auth/msal_provider.go | 27 ++++++++++++--------------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/pkg/client/auth/client.go b/pkg/client/auth/client.go index 7b8ffe2..d9c2327 100644 --- a/pkg/client/auth/client.go +++ b/pkg/client/auth/client.go @@ -1,17 +1,14 @@ package auth import ( - "fmt" - "github.com/AzureAD/microsoft-authentication-library-for-go/apps/public" radixconfig "github.com/equinor/radix-cli/pkg/config" ) // newPublicClient creates a new authentication client -func newPublicClient(radixConfig *radixconfig.RadixConfig, clientID, tenantID string) (*public.Client, error) { +func newPublicClient(radixConfig *radixconfig.RadixConfig, clientID, authority string) (*public.Client, error) { cacheAccessor := NewTokenCache(radixConfig) cache := public.WithCache(cacheAccessor) - authority := fmt.Sprintf("https://login.microsoftonline.com/%s", tenantID) client, err := public.New(clientID, cache, public.WithAuthority(authority)) if err != nil { return nil, err diff --git a/pkg/client/auth/msal_provider.go b/pkg/client/auth/msal_provider.go index 6c65294..436a904 100644 --- a/pkg/client/auth/msal_provider.go +++ b/pkg/client/auth/msal_provider.go @@ -20,17 +20,20 @@ type MSALAuthProvider interface { // NewMSALAuthProvider creates a new MSALAuthProvider func NewMSALAuthProvider(radixConfig *radixconfig.RadixConfig, clientID, tenantID string) (MSALAuthProvider, error) { - client, err := newPublicClient(radixConfig, clientID, tenantID) + authority := fmt.Sprintf("https://login.microsoftonline.com/%s", tenantID) + client, err := newPublicClient(radixConfig, clientID, authority) if err != nil { return nil, err } return &msalAuthProvider{ - client: client, + client: client, + authority: authority, }, nil } type msalAuthProvider struct { - client *public.Client + authority string + client *public.Client } func (provider *msalAuthProvider) AuthenticateRequest(r runtime.ClientRequest, _ strfmt.Registry) error { @@ -44,7 +47,7 @@ func (provider *msalAuthProvider) AuthenticateRequest(r runtime.ClientRequest, _ // Login allows the plugin to initialize its configuration. It must not // require direct user interaction. func (provider *msalAuthProvider) Login(ctx context.Context) error { - _, err := provider.loginWithDeviceCode(ctx) + _, err := provider.loginInteractive(ctx) return err } @@ -80,20 +83,14 @@ func (provider *msalAuthProvider) GetToken(ctx context.Context) (string, error) // either there was no cached account/token or the call to AcquireTokenSilent() failed // make a new request to AAD - return provider.loginWithDeviceCode(ctx) + return provider.loginInteractive(ctx) } -func (provider *msalAuthProvider) loginWithDeviceCode(ctx context.Context) (string, error) { - ctx, cancel := context.WithTimeout(ctx, 100*time.Second) +func (provider *msalAuthProvider) loginInteractive(ctx context.Context) (string, error) { + ctx, cancel := context.WithTimeout(ctx, 10*time.Second) defer cancel() - devCode, err := provider.client.AcquireTokenByDeviceCode(ctx, getScopes()) - if err != nil { - return "", fmt.Errorf("got error while waiting for user to input the device code: %s", err) - } - - fmt.Println(devCode.Result.Message) // show authentication link with device code - - result, err := devCode.AuthenticationResult(ctx) + fmt.Printf("A web browser has been opened at %s/oauth2/v2.0/authorize. Please continue the login in the web browser.\n", provider.authority) + result, err := provider.client.AcquireTokenInteractive(ctx, getScopes()) if err != nil { return "", err } From 85cd52d8abd726c2807497b8b987963712e4e1d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20Gustav=20Str=C3=A5b=C3=B8?= Date: Fri, 12 Apr 2024 13:24:42 +0200 Subject: [PATCH 2/5] add flag --use-device-code for login command --- cmd/login.go | 8 ++++---- pkg/client/auth/msal_provider.go | 29 +++++++++++++++++++++++++---- pkg/client/client.go | 8 ++++---- pkg/flagnames/names.go | 1 + 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/cmd/login.go b/cmd/login.go index 44eb25e..0757cee 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -16,6 +16,7 @@ package cmd import ( "github.com/equinor/radix-cli/pkg/client" + "github.com/equinor/radix-cli/pkg/flagnames" "github.com/spf13/cobra" ) @@ -25,10 +26,8 @@ var loginCmd = &cobra.Command{ Short: "Login to Radix", Long: `Login to Radix.`, RunE: func(cmd *cobra.Command, args []string) error { - - cmd.SilenceUsage = true - - err := client.LoginCommand(cmd) + useDeviceCode, _ := cmd.Flags().GetBool(flagnames.UseDeviceCode) + err := client.LoginCommand(cmd, useDeviceCode) if err != nil { return err } @@ -39,5 +38,6 @@ var loginCmd = &cobra.Command{ func init() { rootCmd.AddCommand(loginCmd) + loginCmd.Flags().Bool(flagnames.UseDeviceCode, false, "Name of the application") setVerbosePersistentFlag(loginCmd) } diff --git a/pkg/client/auth/msal_provider.go b/pkg/client/auth/msal_provider.go index 436a904..8644c58 100644 --- a/pkg/client/auth/msal_provider.go +++ b/pkg/client/auth/msal_provider.go @@ -13,7 +13,7 @@ import ( // MSALAuthProvider is an AuthProvider that uses MSAL type MSALAuthProvider interface { - Login(ctx context.Context) error + Login(ctx context.Context, useDeviceCode bool) error Logout(ctx context.Context) error runtime.ClientAuthInfoWriter } @@ -46,8 +46,12 @@ func (provider *msalAuthProvider) AuthenticateRequest(r runtime.ClientRequest, _ // Login allows the plugin to initialize its configuration. It must not // require direct user interaction. -func (provider *msalAuthProvider) Login(ctx context.Context) error { - _, err := provider.loginInteractive(ctx) +func (provider *msalAuthProvider) Login(ctx context.Context, useDeviceCode bool) error { + var loginCmd func(context.Context) (string, error) = provider.loginInteractive + if useDeviceCode { + loginCmd = provider.loginDeviceCode + } + _, err := loginCmd(ctx) return err } @@ -87,7 +91,7 @@ func (provider *msalAuthProvider) GetToken(ctx context.Context) (string, error) } func (provider *msalAuthProvider) loginInteractive(ctx context.Context) (string, error) { - ctx, cancel := context.WithTimeout(ctx, 10*time.Second) + ctx, cancel := context.WithTimeout(ctx, 100*time.Second) defer cancel() fmt.Printf("A web browser has been opened at %s/oauth2/v2.0/authorize. Please continue the login in the web browser.\n", provider.authority) result, err := provider.client.AcquireTokenInteractive(ctx, getScopes()) @@ -97,6 +101,23 @@ func (provider *msalAuthProvider) loginInteractive(ctx context.Context) (string, return result.AccessToken, nil } +func (provider *msalAuthProvider) loginDeviceCode(ctx context.Context) (string, error) { + ctx, cancel := context.WithTimeout(ctx, 100*time.Second) + defer cancel() + devCode, err := provider.client.AcquireTokenByDeviceCode(ctx, getScopes()) + if err != nil { + return "", fmt.Errorf("got error while waiting for user to input the device code: %s", err) + } + + fmt.Println(devCode.Result.Message) // show authentication link with device code + + result, err := devCode.AuthenticationResult(ctx) + if err != nil { + return "", err + } + return result.AccessToken, nil +} + func getScopes() []string { return []string{"6dae42f8-4368-4678-94ff-3960e28e3630/.default"} } diff --git a/pkg/client/client.go b/pkg/client/client.go index a546b9f..a31a805 100644 --- a/pkg/client/client.go +++ b/pkg/client/client.go @@ -84,8 +84,8 @@ func getAuthWriter(cmd *cobra.Command, config *radixconfig.RadixConfig) (runtime } // LoginCommand Login client for command -func LoginCommand(cmd *cobra.Command) error { - return LoginContext() +func LoginCommand(cmd *cobra.Command, useDeviceCode bool) error { + return LoginContext(useDeviceCode) } // LogoutCommand Logout command @@ -112,7 +112,7 @@ func getContextAndCluster(cmd *cobra.Command) (string, string, error) { } // LoginContext Performs login -func LoginContext() error { +func LoginContext(useDeviceCode bool) error { radixConfig, err := radixconfig.GetRadixConfig() if err != nil { return err @@ -124,7 +124,7 @@ func LoginContext() error { if err != nil { return err } - return provider.Login(context.Background()) + return provider.Login(context.Background(), useDeviceCode) } func getAuthProvider(radixConfig *radixconfig.RadixConfig) (auth.MSALAuthProvider, error) { diff --git a/pkg/flagnames/names.go b/pkg/flagnames/names.go index 65db67b..0109c63 100644 --- a/pkg/flagnames/names.go +++ b/pkg/flagnames/names.go @@ -39,6 +39,7 @@ const ( TokenEnvironment = "token-environment" TokenStdin = "token-stdin" UseActiveDeployment = "use-active-deployment" + UseDeviceCode = "use-device-code" User = "user" Variable = "variable" Verbose = "verbose" From 96baf2c2e6ae05b4206718124be21cf8fa89366f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20Gustav=20Str=C3=A5b=C3=B8?= Date: Fri, 12 Apr 2024 13:57:29 +0200 Subject: [PATCH 3/5] add SilenceUsage and fix usage for flag --- cmd/login.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/login.go b/cmd/login.go index 0757cee..5997b7d 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -26,6 +26,8 @@ var loginCmd = &cobra.Command{ Short: "Login to Radix", Long: `Login to Radix.`, RunE: func(cmd *cobra.Command, args []string) error { + cmd.SilenceUsage = true + useDeviceCode, _ := cmd.Flags().GetBool(flagnames.UseDeviceCode) err := client.LoginCommand(cmd, useDeviceCode) if err != nil { @@ -38,6 +40,6 @@ var loginCmd = &cobra.Command{ func init() { rootCmd.AddCommand(loginCmd) - loginCmd.Flags().Bool(flagnames.UseDeviceCode, false, "Name of the application") + loginCmd.Flags().Bool(flagnames.UseDeviceCode, false, "Use CLI's old authentication flow based on device code") setVerbosePersistentFlag(loginCmd) } From 1fac275ad32cc2c5bf6c67e46323cad1aa7a5a78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20Gustav=20Str=C3=A5b=C3=B8?= Date: Fri, 12 Apr 2024 14:17:17 +0200 Subject: [PATCH 4/5] extend help for use-device-code --- cmd/login.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/login.go b/cmd/login.go index 5997b7d..2705eb1 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -40,6 +40,6 @@ var loginCmd = &cobra.Command{ func init() { rootCmd.AddCommand(loginCmd) - loginCmd.Flags().Bool(flagnames.UseDeviceCode, false, "Use CLI's old authentication flow based on device code") + loginCmd.Flags().Bool(flagnames.UseDeviceCode, false, "Use CLI's old authentication flow based on device code. The device code flow does not work for accounts with compliant device policy enabled.") setVerbosePersistentFlag(loginCmd) } From 55382dd89cd8116b7420c017ae6eb6c5a8518bb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20Gustav=20Str=C3=A5b=C3=B8?= Date: Fri, 12 Apr 2024 14:18:05 +0200 Subject: [PATCH 5/5] update help --- cmd/login.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/login.go b/cmd/login.go index 2705eb1..203a782 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -40,6 +40,6 @@ var loginCmd = &cobra.Command{ func init() { rootCmd.AddCommand(loginCmd) - loginCmd.Flags().Bool(flagnames.UseDeviceCode, false, "Use CLI's old authentication flow based on device code. The device code flow does not work for accounts with compliant device policy enabled.") + loginCmd.Flags().Bool(flagnames.UseDeviceCode, false, "Use CLI's old authentication flow based on device code. The device code flow does not work for compliant device policy enabled accounts.") setVerbosePersistentFlag(loginCmd) }