From 8b01cf1ff5d50cc0f974bc0241d96ad68c7a3abe Mon Sep 17 00:00:00 2001 From: Maina Wycliffe Date: Wed, 11 Dec 2019 15:10:57 +0300 Subject: [PATCH] feat: implement CI login --- cmd/login:ci.go | 34 +++++++++++++++++++++++++++++++ oauth/oauth.go | 53 +++++++++++++++++++++++++++---------------------- 2 files changed, 63 insertions(+), 24 deletions(-) create mode 100644 cmd/login:ci.go diff --git a/cmd/login:ci.go b/cmd/login:ci.go new file mode 100644 index 0000000..903fd1c --- /dev/null +++ b/cmd/login:ci.go @@ -0,0 +1,34 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/logrusorgru/aurora" + "github.com/mainawycliffe/kamanda/oauth" + "github.com/spf13/cobra" +) + +// loginCICmd represents the login:ci command +var loginCICmd = &cobra.Command{ + Use: "login:ci", + Short: "generate an access token for use in non-interactive environments", + Run: func(cmd *cobra.Command, args []string) { + noLocalhostFlag, _ := cmd.Flags().GetBool("no-localhost") + if !noLocalhostFlag { + oauth.LoginWithLocalhost(true) + os.Exit(0) + } + if err := oauth.LoginWithoutLocalhost(true); err != nil { + fmt.Fprint(os.Stdout, aurora.Sprintf(aurora.Red("\n\n%s\n\n"), err.Error())) + os.Exit(1) + } + os.Exit(0) + }, +} + +func init() { + rootCmd.AddCommand(loginCICmd) + // allow users to login without localhost + loginCICmd.Flags().Bool("no-localhost", false, "copy and paste a code instead of starting a local server for authentication") +} diff --git a/oauth/oauth.go b/oauth/oauth.go index 19df581..4f6bf06 100644 --- a/oauth/oauth.go +++ b/oauth/oauth.go @@ -121,9 +121,19 @@ func getGoogleOAuthConfig(port string) *oauth2.Config { } } +func saveRefreshToken(data *GetUserDataFromGoogleResponse) error { + viper.Set(configs.FirebaseRefreshTokenViperConfigKey, data.RefreshToken) + viper.Set(configs.FirebaseLoggedInUserEmailViperConfigKey, data.Email) + err := viper.WriteConfig() + if err != nil { + return fmt.Errorf("An error occurred while saving refresh token: %w", err) + } + return nil +} + // LoginWithLocalhost starts a server that can be used to capture OAUTH // token from Google Auth Server -func LoginWithLocalhost() { +func LoginWithLocalhost(isCILogin bool) { googleOauthConfig := getGoogleOAuthConfig(port) oauthStateTracker := generateOauthStateTracker() u := googleOauthConfig.AuthCodeURL(oauthStateTracker) @@ -156,28 +166,22 @@ func LoginWithLocalhost() { } return } - viper.Set(configs.FirebaseRefreshTokenViperConfigKey, data.RefreshToken) - viper.Set(configs.FirebaseLoggedInUserEmailViperConfigKey, data.Email) - err = viper.WriteConfig() - if err != nil { - fmt.Printf("An error occurred while saving refresh token: %s", err.Error()) - if err := writeHTMLOutput(w, templateData, templates.LoginFailureTemplate); err != nil { - fmt.Printf("Error showing response: %s", err.Error()) - } - return - } - if err != nil { - log.Println(err.Error()) - if err := writeHTMLOutput(w, templateData, templates.LoginFailureTemplate); err != nil { - fmt.Printf("Error showing response: %s", err.Error()) + if isCILogin { + fmt.Fprint(os.Stdout, aurora.Sprintf(aurora.Green("\n\nSuccess! Use this token to login on a CI server:\n\n%s"), data.RefreshToken)) + fmt.Printf("\n\nExample: firebase deploy --token \"$FIREBASE_TOKEN\"\n") + } else { + if err = saveRefreshToken(data); err != nil { + fmt.Printf("%s", err.Error()) + if err := writeHTMLOutput(w, templateData, templates.LoginFailureTemplate); err != nil { + fmt.Printf("Error showing response: %s", err.Error()) + } + return } - return + fmt.Fprint(os.Stdout, aurora.Sprintf(aurora.Green("\n\nSuccess! Logged in as %s\n\n"), data.Email)) } if err := writeHTMLOutput(w, templateData, templates.LoginSuccessTemplate); err != nil { fmt.Printf("Error showing response: %s", err.Error()) } - fmt.Fprint(os.Stdout, aurora.Sprintf(aurora.Green("\n\nSuccess! Logged in as %s\n\n"), data.Email)) - cancel() }) go func() { if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { @@ -193,7 +197,7 @@ func LoginWithLocalhost() { // LoginWithoutLocalhost login without localhost localhost server, suitable in // environments without a GUI. The user enters the authorization code manually -func LoginWithoutLocalhost() error { +func LoginWithoutLocalhost(isCILogin bool) error { googleOauthConfig := getGoogleOAuthConfig("") oauthStateTracker := generateOauthStateTracker() u := googleOauthConfig.AuthCodeURL(oauthStateTracker) @@ -210,11 +214,12 @@ func LoginWithoutLocalhost() error { if err != nil { return fmt.Errorf("An error occurred while exchanging code with token: %w", err) } - viper.Set(configs.FirebaseRefreshTokenViperConfigKey, data.RefreshToken) - viper.Set(configs.FirebaseLoggedInUserEmailViperConfigKey, data.Email) - err = viper.WriteConfig() - if err != nil { - return fmt.Errorf("An error occurred while saving refresh token: %w", err) + if isCILogin { + fmt.Fprint(os.Stdout, aurora.Sprintf(aurora.Green("\n\nSuccess! Use this token to login on a CI server:\n\n%s\n"), data.RefreshToken)) + return nil + } + if err = saveRefreshToken(data); err != nil { + return err } fmt.Fprint(os.Stdout, aurora.Sprintf(aurora.Green("\n\nSuccess! Logged in as %s\n\n"), data.Email)) return nil