Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace device code login with interactive login #90

Merged
merged 5 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions cmd/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand All @@ -25,10 +26,10 @@ 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
}
Expand All @@ -39,5 +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 compliant device policy enabled accounts.")
setVerbosePersistentFlag(loginCmd)
}
5 changes: 1 addition & 4 deletions pkg/client/auth/client.go
Original file line number Diff line number Diff line change
@@ -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
Expand Down
34 changes: 26 additions & 8 deletions pkg/client/auth/msal_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,27 @@ 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
}

// 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 {
Expand All @@ -43,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.loginWithDeviceCode(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
}

Expand Down Expand Up @@ -80,10 +87,21 @@ 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) loginInteractive(ctx context.Context) (string, error) {
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())
if err != nil {
return "", err
}
return result.AccessToken, nil
}

func (provider *msalAuthProvider) loginWithDeviceCode(ctx context.Context) (string, error) {
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())
Expand Down
8 changes: 4 additions & 4 deletions pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions pkg/flagnames/names.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down