Skip to content

Commit

Permalink
Enhance auth-login - open browser on wsl (#2576)
Browse files Browse the repository at this point in the history
  • Loading branch information
vhvb1989 authored Aug 2, 2023
1 parent 6339ba7 commit c862ff2
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 24 deletions.
10 changes: 9 additions & 1 deletion cli/azd/cmd/auth_login.go
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,15 @@ func (la *loginAction) login(ctx context.Context) error {
return fmt.Errorf("logging in: %w", err)
}
} else {
_, err := la.authManager.LoginInteractive(ctx, la.flags.redirectPort, la.flags.tenantID, la.flags.scopes)
_, err := la.authManager.LoginInteractive(ctx, la.flags.scopes,
&auth.LoginInteractiveOptions{
TenantID: la.flags.tenantID,
RedirectPort: la.flags.redirectPort,
WithOpenUrl: func(url string) error {
openWithDefaultBrowser(ctx, la.console, url)
return nil
},
})
if err != nil {
return fmt.Errorf("logging in: %w", err)
}
Expand Down
52 changes: 42 additions & 10 deletions cli/azd/cmd/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import (
"context"
"errors"
"fmt"
"log"
"os"
"os/exec"
"strings"
"time"

"github.com/AlecAivazis/survey/v2"
Expand All @@ -17,6 +18,7 @@ import (
"github.com/azure/azure-dev/cli/azd/internal/tracing"
"github.com/azure/azure-dev/cli/azd/pkg/environment"
"github.com/azure/azure-dev/cli/azd/pkg/environment/azdcontext"
azdExec "github.com/azure/azure-dev/cli/azd/pkg/exec"
"github.com/azure/azure-dev/cli/azd/pkg/input"
"github.com/azure/azure-dev/cli/azd/pkg/output"
"github.com/azure/azure-dev/cli/azd/pkg/project"
Expand Down Expand Up @@ -328,22 +330,24 @@ func openWithDefaultBrowser(ctx context.Context, console input.Console, url stri
return
}

console.Message(ctx, fmt.Sprintf("Opening %s in the default browser...\n", url))
cmdRunner := azdExec.NewCommandRunner(nil)

// In Codespaces and devcontainers a $BROWSER environment variable is
// present whose value is an executable that launches the browser when
// called with the form:
// $BROWSER <url>

const BrowserEnvVarName = "BROWSER"

if envBrowser := os.Getenv(BrowserEnvVarName); len(envBrowser) > 0 {
err := exec.Command(envBrowser, url).Run()
_, err := cmdRunner.Run(ctx, azdExec.RunArgs{
Cmd: envBrowser,
Args: []string{url},
})
if err == nil {
return
}
fmt.Fprintf(
console.Handles().Stderr,
"warning: failed to open browser configured by $BROWSER: %s\n. Trying with default browser.",
log.Printf(
"warning: failed to open browser configured by $BROWSER: %s\nTrying with default browser.\n",
err.Error(),
)
}
Expand All @@ -353,11 +357,39 @@ func openWithDefaultBrowser(ctx context.Context, console input.Console, url stri
return
}

fmt.Fprintf(
console.Handles().Stderr,
"warning: failed to open default browser: %s\n", err.Error(),
log.Printf(
"warning: failed to open default browser: %s\nTrying manual launch.", err.Error(),
)

// wsl manual launch. Trying cmd first, and pwsh second
_, err = cmdRunner.Run(ctx, azdExec.RunArgs{
Cmd: "cmd.exe",
// cmd notes:
// /c -> run command
// /s -> quoted string, use the text within the quotes as it is
// Replace `&` for `^&` -> cmd require to scape the & to avoid using it as a command concat
Args: []string{
"/s", "/c", fmt.Sprintf("start %s", strings.ReplaceAll(url, "&", "^&")),
},
})
if err == nil {
return
}
log.Printf(
"warning: failed to open browser with cmd: %s\nTrying powershell.", err.Error(),
)

_, err = cmdRunner.Run(ctx, azdExec.RunArgs{
Cmd: "powershell.exe",
Args: []string{
"-NoProfile", "-Command", "Start-Process", fmt.Sprintf("\"%s\"", url),
},
})
if err == nil {
return
}

log.Printf("warning: failed to use manual launch: %s\n", err.Error())
console.Message(ctx, fmt.Sprintf("Azd was unable to open the next url. Please try it manually: %s", url))
}

Expand Down
36 changes: 29 additions & 7 deletions cli/azd/pkg/auth/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -443,21 +443,43 @@ func (m *Manager) newCredentialFromCloudShell() (azcore.TokenCredential, error)
return NewCloudShellCredential(m.httpClient), nil
}

// WithOpenUrl defines a custom strategy for browsing to the url.
type WithOpenUrl func(url string) error

// LoginInteractiveOptions holds the optional inputs for interactive login.
type LoginInteractiveOptions struct {
TenantID string
RedirectPort int
WithOpenUrl WithOpenUrl
}

// LoginInteractive opens a browser for authenticate the user.
func (m *Manager) LoginInteractive(
ctx context.Context, redirectPort int, tenantID string, scopes []string) (azcore.TokenCredential, error) {
ctx context.Context,
scopes []string,
options *LoginInteractiveOptions) (azcore.TokenCredential, error) {
if scopes == nil {
scopes = LoginScopes
}
options := []public.AcquireInteractiveOption{}
if redirectPort > 0 {
options = append(options, public.WithRedirectURI(fmt.Sprintf("http://localhost:%d", redirectPort)))
acquireTokenOptions := []public.AcquireInteractiveOption{}
if options == nil {
options = &LoginInteractiveOptions{}
}

if tenantID != "" {
options = append(options, public.WithTenantID(tenantID))
if options.RedirectPort > 0 {
acquireTokenOptions = append(
acquireTokenOptions, public.WithRedirectURI(fmt.Sprintf("http://localhost:%d", options.RedirectPort)))
}

if options.TenantID != "" {
acquireTokenOptions = append(acquireTokenOptions, public.WithTenantID(options.TenantID))
}

if options.WithOpenUrl != nil {
acquireTokenOptions = append(acquireTokenOptions, public.WithOpenURL(options.WithOpenUrl))
}

res, err := m.publicClient.AcquireTokenInteractive(ctx, scopes, options...)
res, err := m.publicClient.AcquireTokenInteractive(ctx, scopes, acquireTokenOptions...)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion cli/azd/pkg/auth/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ func TestLoginInteractive(t *testing.T) {
publicClient: &mockPublicClient{},
}

cred, err := m.LoginInteractive(context.Background(), 0, "", nil)
cred, err := m.LoginInteractive(context.Background(), nil, nil)

require.NoError(t, err)
require.IsType(t, new(azdCredential), cred)
Expand Down
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ require (
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armsubscriptions v1.0.0
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v0.13.0
github.com/Azure/azure-storage-file-go v0.8.0
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0
github.com/MakeNowJust/heredoc/v2 v2.0.1
github.com/benbjohnson/clock v1.3.0
github.com/blang/semver/v4 v4.0.0
Expand Down Expand Up @@ -60,6 +60,8 @@ require github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appcontainers/arma

require github.com/adam-lavrik/go-imath v0.0.0-20210910152346-265a42a96f0b

require github.com/golang-jwt/jwt/v5 v5.0.0 // indirect

require (
github.com/Azure/azure-pipeline-go v0.2.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
Expand All @@ -71,7 +73,6 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-logr/logr v1.2.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
Expand Down
8 changes: 5 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0 h1:T028g
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0/go.mod h1:cw4zVQgBby0Z5f2v0itn6se2dDP17nTjbZFXW5uPyHA=
github.com/Azure/azure-storage-file-go v0.8.0 h1:OX8DGsleWLUE6Mw4R/OeWEZMvsTIpwN94J59zqKQnTI=
github.com/Azure/azure-storage-file-go v0.8.0/go.mod h1:3w3mufGcMjcOJ3w+4Gs+5wsSgkT7xDwWWqMMIrXtW4c=
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkMqLPymWEppkm7vgPQY2XsHoEkaMQ0AdZY=
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0 h1:HCc0+LpPfpCKs6LGGLAhwBARt9632unrVcI6i8s/8os=
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
Expand Down Expand Up @@ -193,8 +193,10 @@ github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14j
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
Expand Down

0 comments on commit c862ff2

Please sign in to comment.