From b0195ee12b58ada1e43f8bb9ef5d4b64933ef8b1 Mon Sep 17 00:00:00 2001 From: Mark Phelps <209477+markphelps@users.noreply.github.com> Date: Tue, 28 May 2024 13:36:20 -0400 Subject: [PATCH 1/2] chore: prompt for login if no token Signed-off-by: Mark Phelps <209477+markphelps@users.noreply.github.com> --- cmd/flipt/cloud.go | 154 +++++++++++++++++++++++++++------------------ 1 file changed, 94 insertions(+), 60 deletions(-) diff --git a/cmd/flipt/cloud.go b/cmd/flipt/cloud.go index 69a26a09fa..9ed4c9639e 100644 --- a/cmd/flipt/cloud.go +++ b/cmd/flipt/cloud.go @@ -111,67 +111,13 @@ func (c *cloudCommand) login(cmd *cobra.Command, args []string) error { return fmt.Errorf("parsing cloud URL: %w", err) } - ok, err := util.PromptConfirm("Open browser to authenticate with Flipt Cloud?", false) - if err != nil { + if _, err := c.loginFlow(ctx); err != nil { return err } - if !ok { - return nil - } - - flow, err := cloud.InitFlow() - if err != nil { - return fmt.Errorf("initializing flow: %w", err) - } - - defer flow.Close() - - var g errgroup.Group - - g.Go(func() error { - if err := flow.StartServer(nil); err != nil && !errors.Is(err, net.ErrClosed) { - return fmt.Errorf("starting server: %w", err) - } - return nil - }) - - url, err := flow.BrowserURL(fmt.Sprintf("%s/login/device", c.url)) - if err != nil { - return fmt.Errorf("creating browser URL: %w", err) - } - - if err := util.OpenBrowser(url); err != nil { - return fmt.Errorf("opening browser: %w", err) - } - - cloudAuthFile := filepath.Join(userConfigDir, "cloud.json") - - tok, err := flow.Wait(ctx) - if err != nil { - return fmt.Errorf("waiting for token: %w", err) - } - - if err := flow.Close(); err != nil && !errors.Is(err, net.ErrClosed) { - return fmt.Errorf("closing flow: %w", err) - } - - cloudAuth := cloudAuth{ - Token: tok, - } - - cloudAuthBytes, err := json.Marshal(cloudAuth) - if err != nil { - return fmt.Errorf("marshalling cloud auth token: %w", err) - } - - if err := os.WriteFile(cloudAuthFile, cloudAuthBytes, 0600); err != nil { - return fmt.Errorf("writing cloud auth token: %w", err) - } - fmt.Println("\n✓ Authenticated with Flipt Cloud!\nYou can now run commands that require cloud authentication.") - return g.Wait() + return nil } func (c *cloudCommand) serve(cmd *cobra.Command, args []string) error { @@ -204,12 +150,26 @@ func (c *cloudCommand) serve(cmd *cobra.Command, args []string) error { // first check for existing of auth token/cloud.json // if not found, prompt user to login + +AUTH: cloudAuthFile := filepath.Join(userConfigDir, "cloud.json") f, err := os.ReadFile(cloudAuthFile) if err != nil { if errors.Is(err, os.ErrNotExist) { - fmt.Println("\n✗ No cloud authentication token found. Please run 'flipt cloud login' to authenticate.") - return nil + fmt.Println("\n✗ No cloud authentication token found.") + + ok, err := c.loginFlow(ctx) + if err != nil { + return err + } + + // if they didn't attempt login, exit + if !ok { + return nil + } + + // otherwise, try reading the file again + goto AUTH } return fmt.Errorf("reading cloud auth payload %w", err) @@ -234,8 +194,20 @@ func (c *cloudCommand) serve(cmd *cobra.Command, args []string) error { parsed, err := jwt.Parse(auth.Token, k.Keyfunc, jwt.WithExpirationRequired()) if err != nil { if errors.Is(err, jwt.ErrTokenExpired) { - fmt.Println("✗ Existing cloud authentication token expired. Please run 'flipt cloud login' to re-authenticate.") - return nil + fmt.Println("✗ Existing cloud authentication token expired.") + + ok, err := c.loginFlow(ctx) + if err != nil { + return err + } + + // if they didn't attempt login, exit + if !ok { + return nil + } + + // otherwise, try reading the file again + goto AUTH } return fmt.Errorf("parsing JWT: %w", err) @@ -375,3 +347,65 @@ func (c *cloudCommand) serve(cmd *cobra.Command, args []string) error { fmt.Println("✓ Starting local instance linked with Flipt Cloud.") return run(ctx, logger, srvConfig(cfg, instance)) } + +func (c *cloudCommand) loginFlow(ctx context.Context) (bool, error) { + ok, err := util.PromptConfirm("Open browser to authenticate with Flipt Cloud?", false) + if err != nil { + return false, err + } + + if !ok { + return false, nil + } + + flow, err := cloud.InitFlow() + if err != nil { + return true, fmt.Errorf("initializing flow: %w", err) + } + + defer flow.Close() + + var g errgroup.Group + + g.Go(func() error { + if err := flow.StartServer(nil); err != nil && !errors.Is(err, net.ErrClosed) { + return fmt.Errorf("starting server: %w", err) + } + return nil + }) + + url, err := flow.BrowserURL(fmt.Sprintf("%s/login/device", c.url)) + if err != nil { + return true, fmt.Errorf("creating browser URL: %w", err) + } + + if err := util.OpenBrowser(url); err != nil { + return true, fmt.Errorf("opening browser: %w", err) + } + + cloudAuthFile := filepath.Join(userConfigDir, "cloud.json") + + tok, err := flow.Wait(ctx) + if err != nil { + return true, fmt.Errorf("waiting for token: %w", err) + } + + if err := flow.Close(); err != nil && !errors.Is(err, net.ErrClosed) { + return true, fmt.Errorf("closing flow: %w", err) + } + + cloudAuth := cloudAuth{ + Token: tok, + } + + cloudAuthBytes, err := json.Marshal(cloudAuth) + if err != nil { + return true, fmt.Errorf("marshalling cloud auth token: %w", err) + } + + if err := os.WriteFile(cloudAuthFile, cloudAuthBytes, 0600); err != nil { + return true, fmt.Errorf("writing cloud auth token: %w", err) + } + + return true, g.Wait() +} From e2a5c42a3681a9f62e09fc10e9e66018af8b76e9 Mon Sep 17 00:00:00 2001 From: Mark Phelps <209477+markphelps@users.noreply.github.com> Date: Tue, 28 May 2024 13:44:24 -0400 Subject: [PATCH 2/2] chore: refactor login prompt --- cmd/flipt/cloud.go | 50 +++++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/cmd/flipt/cloud.go b/cmd/flipt/cloud.go index 9ed4c9639e..24b0523611 100644 --- a/cmd/flipt/cloud.go +++ b/cmd/flipt/cloud.go @@ -111,7 +111,17 @@ func (c *cloudCommand) login(cmd *cobra.Command, args []string) error { return fmt.Errorf("parsing cloud URL: %w", err) } - if _, err := c.loginFlow(ctx); err != nil { + ok, err := util.PromptConfirm("Open browser to authenticate with Flipt Cloud?", false) + if err != nil { + return err + } + + // if they didn't attempt login, exit + if !ok { + return nil + } + + if err := c.loginFlow(ctx); err != nil { return err } @@ -158,7 +168,7 @@ AUTH: if errors.Is(err, os.ErrNotExist) { fmt.Println("\n✗ No cloud authentication token found.") - ok, err := c.loginFlow(ctx) + ok, err := util.PromptConfirm("Open browser to authenticate with Flipt Cloud?", false) if err != nil { return err } @@ -168,6 +178,10 @@ AUTH: return nil } + if err := c.loginFlow(ctx); err != nil { + return err + } + // otherwise, try reading the file again goto AUTH } @@ -196,7 +210,7 @@ AUTH: if errors.Is(err, jwt.ErrTokenExpired) { fmt.Println("✗ Existing cloud authentication token expired.") - ok, err := c.loginFlow(ctx) + ok, err := util.PromptConfirm("Open browser to authenticate with Flipt Cloud?", false) if err != nil { return err } @@ -206,6 +220,10 @@ AUTH: return nil } + if err := c.loginFlow(ctx); err != nil { + return err + } + // otherwise, try reading the file again goto AUTH } @@ -348,19 +366,11 @@ AUTH: return run(ctx, logger, srvConfig(cfg, instance)) } -func (c *cloudCommand) loginFlow(ctx context.Context) (bool, error) { - ok, err := util.PromptConfirm("Open browser to authenticate with Flipt Cloud?", false) - if err != nil { - return false, err - } - - if !ok { - return false, nil - } +func (c *cloudCommand) loginFlow(ctx context.Context) error { flow, err := cloud.InitFlow() if err != nil { - return true, fmt.Errorf("initializing flow: %w", err) + return fmt.Errorf("initializing flow: %w", err) } defer flow.Close() @@ -376,22 +386,22 @@ func (c *cloudCommand) loginFlow(ctx context.Context) (bool, error) { url, err := flow.BrowserURL(fmt.Sprintf("%s/login/device", c.url)) if err != nil { - return true, fmt.Errorf("creating browser URL: %w", err) + return fmt.Errorf("creating browser URL: %w", err) } if err := util.OpenBrowser(url); err != nil { - return true, fmt.Errorf("opening browser: %w", err) + return fmt.Errorf("opening browser: %w", err) } cloudAuthFile := filepath.Join(userConfigDir, "cloud.json") tok, err := flow.Wait(ctx) if err != nil { - return true, fmt.Errorf("waiting for token: %w", err) + return fmt.Errorf("waiting for token: %w", err) } if err := flow.Close(); err != nil && !errors.Is(err, net.ErrClosed) { - return true, fmt.Errorf("closing flow: %w", err) + return fmt.Errorf("closing flow: %w", err) } cloudAuth := cloudAuth{ @@ -400,12 +410,12 @@ func (c *cloudCommand) loginFlow(ctx context.Context) (bool, error) { cloudAuthBytes, err := json.Marshal(cloudAuth) if err != nil { - return true, fmt.Errorf("marshalling cloud auth token: %w", err) + return fmt.Errorf("marshalling cloud auth token: %w", err) } if err := os.WriteFile(cloudAuthFile, cloudAuthBytes, 0600); err != nil { - return true, fmt.Errorf("writing cloud auth token: %w", err) + return fmt.Errorf("writing cloud auth token: %w", err) } - return true, g.Wait() + return g.Wait() }