From 7635aeee5ffd4f9fb5c141cb89e48ec52a3b0035 Mon Sep 17 00:00:00 2001 From: pacoorozco Date: Thu, 26 Oct 2023 19:24:13 +0200 Subject: [PATCH] Add a parameter to the command to set the redirect URL after auth Signed-off-by: pacoorozco --- CHANGELOG.md | 6 ++++++ internal/app/oauth.go | 23 ++++++++++++++++++----- internal/cli/auth/auth.go | 18 +++++++++++++++--- internal/oauth/oauth.go | 21 +++++++++++++-------- 4 files changed, 52 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58a9abb..3dc92c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/) and this project adheres to [Semantic Versioning](https://semver.org/). +## 4.2.0 +### Added +- New parameter `--redirect-url-hostname` to the `auth` command in order to set the URL to use after the Google Photos authentication ([#402][i402]) + +[i402]: https://github.com/gphotosuploader/gphotos-uploader-cli/issues/402 + ## 4.1.1 ### Fixed - Uploads are showing the data of upload instead of the filename in the Google Photos UI. Thanks [@mikebilly](https://github.com/mikebilly) ([#398][i398]) diff --git a/internal/app/oauth.go b/internal/app/oauth.go index badf648..04eb07e 100644 --- a/internal/app/oauth.go +++ b/internal/app/oauth.go @@ -39,17 +39,30 @@ func (app *App) AuthenticateFromToken(ctx context.Context) (*http.Client, error) return oauth.Client(ctx, cfg, token) } +type AuthenticationOptions struct { + // Hostname of the redirect URL. + // You can set this if your provider does not accept localhost. + // Default to localhost. + RedirectURLHostname string + + // Hostname and port which the local server binds to. + // You can set port number to 0 to allocate a free port. + // If nil or an empty slice is given, it defaults to "127.0.0.1:0" i.e., a free port. + LocalServerBindAddress string +} + // AuthenticateFromWeb returns an HTTP client authenticated in Google Photos. // AuthenticateFromWeb will create a new token after completing the OAuth 2.0 flow. -func (app *App) AuthenticateFromWeb(ctx context.Context, port int) (*http.Client, error) { +func (app *App) AuthenticateFromWeb(ctx context.Context, authOptions AuthenticationOptions) (*http.Client, error) { account := app.Config.Account app.Logger.Infof("Getting authentication token for '%s'", account) cfg := &oauth.Config{ - ClientID: app.Config.APIAppCredentials.ClientID, - ClientSecret: app.Config.APIAppCredentials.ClientSecret, - Logf: app.Logger.Debugf, - Port: port, + ClientID: app.Config.APIAppCredentials.ClientID, + ClientSecret: app.Config.APIAppCredentials.ClientSecret, + Logf: app.Logger.Debugf, + LocalServerBindAddress: []string{authOptions.LocalServerBindAddress}, + RedirectURLHostname: authOptions.RedirectURLHostname, } token, err := oauth.GetToken(ctx, cfg) diff --git a/internal/cli/auth/auth.go b/internal/cli/auth/auth.go index c42db16..9d6b8ae 100644 --- a/internal/cli/auth/auth.go +++ b/internal/cli/auth/auth.go @@ -2,6 +2,7 @@ package auth import ( "context" + "fmt" "github.com/spf13/cobra" @@ -14,7 +15,8 @@ type AuthCmd struct { *flags.GlobalFlags // command flags - Port int + Port int + RedirectURLHostname string } func NewCommand(globalFlags *flags.GlobalFlags) *cobra.Command { @@ -28,7 +30,8 @@ func NewCommand(globalFlags *flags.GlobalFlags) *cobra.Command { RunE: cmd.Run, } - authCmd.Flags().IntVarP(&cmd.Port, "port", "p", 0, "port on which the auth server will listen (default 0)") + authCmd.Flags().IntVar(&cmd.Port, "port", 0, "port on which the auth server will listen (default 0)") + authCmd.Flags().StringVar(&cmd.RedirectURLHostname, "redirect-url-hostname", "", "hostname of the redirect URL (default localhost)") return authCmd } @@ -43,7 +46,16 @@ func (cmd *AuthCmd) Run(cobraCmd *cobra.Command, args []string) error { _ = cli.Stop() }() - _, err = cli.AuthenticateFromWeb(ctx, cmd.Port) + // customize authentication options based on the command line parameters + authOptions := app.AuthenticationOptions{} + if cmd.Port != 0 { + authOptions.LocalServerBindAddress = fmt.Sprintf("127.0.0.1:%d", cmd.Port) + } + if cmd.RedirectURLHostname != "" { + authOptions.RedirectURLHostname = cmd.RedirectURLHostname + } + + _, err = cli.AuthenticateFromWeb(ctx, authOptions) if err == nil { cli.Logger.Donef("Successful authentication for account '%s'", cli.Config.Account) } diff --git a/internal/oauth/oauth.go b/internal/oauth/oauth.go index a41a9d0..d657a51 100644 --- a/internal/oauth/oauth.go +++ b/internal/oauth/oauth.go @@ -32,8 +32,16 @@ type Config struct { // Logger function for debug. Logf func(format string, args ...interface{}) - // Port to bind the local authenticator - Port int + // Candidates of hostname and port which the local server binds to. + // You can set port number to 0 to allocate a free port. + // If multiple addresses are given, it will try the ports in order. + // If nil or an empty slice is given, it defaults to "127.0.0.1:0" i.e., a free port. + LocalServerBindAddress []string + + // Hostname of the redirect URL. + // You can set this if your provider does not accept localhost. + // Default to localhost. + RedirectURLHostname string oAuth2Config *oauth2.Config } @@ -69,10 +77,6 @@ func (c *Config) validateAndSetDefaults() error { return fmt.Errorf("both ClientID and ClientSecret must be set") } - if c.Port < 0 || c.Port > 65535 { - return fmt.Errorf("invalid port, must be in the range [0-65535]") - } - if c.Logf == nil { c.Logf = func(string, ...interface{}) {} } @@ -82,6 +86,7 @@ func (c *Config) validateAndSetDefaults() error { ClientSecret: c.ClientSecret, Scopes: []string{PhotosLibraryScope}, Endpoint: GoogleAuthEndpoint, + RedirectURL: c.RedirectURLHostname, } return nil @@ -91,12 +96,12 @@ func (c *Config) validateAndSetDefaults() error { // flow, blocks until the user completes authorization and is redirected back, and returns the access token. func (c *Config) getTokenFromWeb(ctx context.Context) (*oauth2.Token, error) { ready := make(chan string, 1) - localServerBindAddress := fmt.Sprintf("127.0.0.1:%d", c.Port) cfg := oauth2cli.Config{ OAuth2Config: *c.oAuth2Config, LocalServerReadyChan: ready, Logf: c.Logf, - LocalServerBindAddress: []string{localServerBindAddress}, + LocalServerBindAddress: c.LocalServerBindAddress, + RedirectURLHostname: c.RedirectURLHostname, } var token *oauth2.Token