Client Login via device authorization flow (#309)

UI and CLI Clients are now able to use SSO login by default

we will check if the management has configured or supports SSO providers

daemon will handle fetching and waiting for an access token

Oauth package was moved to internal to avoid one extra package at this stage

Secrets were removed from OAuth

CLI clients have less and better output

2 new status were introduced, NeedsLogin and FailedLogin for better messaging

With NeedsLogin we no longer have endless login attempts
This commit is contained in:
Maycon Santos
2022-05-12 11:17:24 +02:00
committed by GitHub
parent 49cca57565
commit e5c52efb4c
26 changed files with 925 additions and 427 deletions

View File

@@ -2,13 +2,13 @@ package cmd
import (
"context"
"github.com/netbirdio/netbird/util"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"fmt"
"github.com/netbirdio/netbird/client/internal"
"github.com/netbirdio/netbird/client/proto"
"github.com/netbirdio/netbird/util"
"github.com/spf13/cobra"
"google.golang.org/grpc/codes"
gstatus "google.golang.org/grpc/status"
)
var upCmd = &cobra.Command{
@@ -17,10 +17,9 @@ var upCmd = &cobra.Command{
RunE: func(cmd *cobra.Command, args []string) error {
SetFlagsFromEnvVars()
err := util.InitLog(logLevel, logFile)
err := util.InitLog(logLevel, "console")
if err != nil {
log.Errorf("failed initializing log %v", err)
return err
return fmt.Errorf("failed initializing log %v", err)
}
ctx := internal.CtxInitState(cmd.Context())
@@ -29,15 +28,12 @@ var upCmd = &cobra.Command{
if logFile == "console" {
config, err := internal.GetConfig(managementURL, adminURL, configPath, preSharedKey)
if err != nil {
log.Errorf("get config file: %v", err)
return err
return fmt.Errorf("get config file: %v", err)
}
err = WithBackOff(func() error {
return internal.Login(ctx, config, setupKey, jwtToken)
})
err = foregroundLogin(ctx, cmd, config, setupKey)
if err != nil {
log.Errorf("backoff cycle failed: %v", err)
return err
return fmt.Errorf("foreground login failed: %v", err)
}
var cancel context.CancelFunc
@@ -48,43 +44,67 @@ var upCmd = &cobra.Command{
conn, err := DialClientGRPCServer(ctx, daemonAddr)
if err != nil {
log.Errorf("failed to connect to service CLI interface %v", err)
return err
return fmt.Errorf("failed to connect to daemon error: %v\n"+
"If the daemon is not running please run: "+
"\nnetbird service install \nnetbird service start\n", err)
}
defer conn.Close()
daemonClient := proto.NewDaemonServiceClient(conn)
client := proto.NewDaemonServiceClient(conn)
loginRequest := proto.LoginRequest{
SetupKey: setupKey,
PreSharedKey: preSharedKey,
ManagementUrl: managementURL,
}
err = WithBackOff(func() error {
_, err := daemonClient.Login(ctx, &loginRequest)
return err
})
status, err := client.Status(ctx, &proto.StatusRequest{})
if err != nil {
log.Errorf("backoff cycle failed: %v", err)
return err
return fmt.Errorf("unable to get daemon status: %v", err)
}
status, err := daemonClient.Status(ctx, &proto.StatusRequest{})
if err != nil {
log.Errorf("get status: %v", err)
return err
}
if status.Status == string(internal.StatusNeedsLogin) || status.Status == string(internal.StatusLoginFailed) {
loginRequest := proto.LoginRequest{
SetupKey: setupKey,
PreSharedKey: preSharedKey,
ManagementUrl: managementURL,
}
if status.Status != string(internal.StatusIdle) {
log.Warnf("already connected")
var loginErr error
var loginResp *proto.LoginResponse
err = WithBackOff(func() error {
var backOffErr error
loginResp, backOffErr = client.Login(ctx, &loginRequest)
if s, ok := gstatus.FromError(backOffErr); ok && (s.Code() == codes.InvalidArgument ||
s.Code() == codes.PermissionDenied ||
s.Code() == codes.NotFound ||
s.Code() == codes.Unimplemented) {
loginErr = backOffErr
return nil
}
return backOffErr
})
if err != nil {
return fmt.Errorf("login backoff cycle failed: %v", err)
}
if loginErr != nil {
return fmt.Errorf("login failed: %v", loginErr)
}
if loginResp.NeedsSSOLogin {
openURL(cmd, loginResp.VerificationURI, loginResp.VerificationURIComplete, loginResp.UserCode)
_, err = client.WaitSSOLogin(ctx, &proto.WaitSSOLoginRequest{UserCode: loginResp.UserCode})
if err != nil {
return fmt.Errorf("waiting sso login failed with: %v", err)
}
}
} else if status.Status != string(internal.StatusIdle) {
cmd.Println("Already connected")
return nil
}
if _, err := daemonClient.Up(ctx, &proto.UpRequest{}); err != nil {
log.Errorf("call service up method: %v", err)
return err
if _, err := client.Up(ctx, &proto.UpRequest{}); err != nil {
return fmt.Errorf("call service up method: %v", err)
}
cmd.Println("Connected")
return nil
},
}