From 807d4a3c00e78c0067fd5c41f5f246e590f17d27 Mon Sep 17 00:00:00 2001 From: klauspost Date: Fri, 11 Sep 2015 14:26:51 +0200 Subject: [PATCH] Improve OAUTH2 usability - fixes #131 Shorten the URL to be used by the user and automatically use the returned code by the server. The browser is opened on `http://(bindaddress)/auth`, and redirected to the actual URL. When the code is returned it is automatically inserted, instead of requiring a copy+paste. This is also a workaround for the "open" package, which escapes "&" wrongly on Windows, so the opened URL's are invalid. --- docs/content/amazonclouddrive.md | 12 ++++----- oauthutil/oauthutil.go | 45 ++++++++++++++++++++++++++------ 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/docs/content/amazonclouddrive.md b/docs/content/amazonclouddrive.md index 492806ea0..0d5238ad7 100644 --- a/docs/content/amazonclouddrive.md +++ b/docs/content/amazonclouddrive.md @@ -42,10 +42,10 @@ client_id> Amazon Application Client Secret - leave blank to use rclone's. client_secret> Remote config -If your browser doesn't open automatically go to the following link -https://www.amazon.com/ap/oa?client_id=amzn1.application-oa2-client.xxxxxxxxxxxxxxx&redirect_uri=http%3A%2F%2F127.0.0.1%3A53682%2F&response_type=code&scope=clouddrive%3Aread_all+clouddrive%3Awrite&state=xxxxxxxxxxxxxxxxx -Log in, then cut and paste the token that is returned from the browser here -Enter verification code> xxxxxxxxxxxxxxxxxxxx +If your browser doesn't open automatically go to the following link: http://127.0.0.1:53682/auth +Log in and authorize rclone for access +Waiting for code... +Got code -------------------- [remote] client_id = @@ -59,8 +59,8 @@ y/e/d> y ``` Note that rclone runs a webserver on your local machine to collect the -token as returned from Amazon. This is only run from the moment it -opens your browser to the moment you cut and paste the verification +token as returned from Amazon. This only runs from the moment it +opens your browser to the moment you get back the verification code. This is on `http://127.0.0.1:53682/` and this it may require you to unblock it temporarily if you are running a host firewall. diff --git a/oauthutil/oauthutil.go b/oauthutil/oauthutil.go index 9a4496290..7718d3062 100644 --- a/oauthutil/oauthutil.go +++ b/oauthutil/oauthutil.go @@ -162,27 +162,41 @@ func ConfigWithWebserver(name string, config *oauth2.Config, bindAddress string) return err } state := fmt.Sprintf("%x", stateBytes) + authUrl := config.AuthCodeURL(state) // Prepare webserver server := authServer{ state: state, bindAddress: bindAddress, + authUrl: authUrl, } if bindAddress != "" { + server.code = make(chan string, 1) go server.Start() defer server.Stop() + authUrl = "http://" + bindAddress + "/auth" } // Generate a URL for the user to visit for authorization. - authUrl := config.AuthCodeURL(state) _ = open.Start(authUrl) - fmt.Printf("If your browser doesn't open automatically go to the following link\n") - fmt.Printf("%s\n", authUrl) - fmt.Printf("Log in, then cut and paste the token that is returned from the browser here\n") + fmt.Printf("If your browser doesn't open automatically go to the following link: %s\n") + fmt.Printf("Log in and authorize rclone for access\n") - // Read the code, and exchange it for a token. - fmt.Printf("Enter verification code> ") - authCode := fs.ReadLine() + var authCode string + if bindAddress != "" { + // Read the code, and exchange it for a token. + fmt.Printf("Waiting for code...\n") + authCode = <-server.code + if authCode != "" { + fmt.Printf("Got code\n") + } else { + return fmt.Errorf("Failed to get code") + } + } else { + // Read the code, and exchange it for a token. + fmt.Printf("Enter verification code> ") + authCode = fs.ReadLine() + } token, err := config.Exchange(oauth2.NoContext, authCode) if err != nil { return fmt.Errorf("Failed to get token: %v", err) @@ -200,6 +214,8 @@ type authServer struct { state string listener net.Listener bindAddress string + code chan string + authUrl string } // startWebServer runs an internal web server to receive config details @@ -214,6 +230,10 @@ func (s *authServer) Start() { http.Error(w, "", 404) return }) + mux.HandleFunc("/auth", func(w http.ResponseWriter, req *http.Request) { + http.Redirect(w, req, s.authUrl, 307) + return + }) mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { fs.Debug(nil, "Received request on auth server") code := req.FormValue("code") @@ -224,7 +244,12 @@ func (s *authServer) Start() { fmt.Fprintf(w, "

Failure

\n

Auth state doesn't match

") } else { fs.Debug(nil, "Successfully got code") - fmt.Fprintf(w, "

Success

\n

Cut and paste this code into rclone: %s

", code) + if s.code != nil { + fmt.Fprintf(w, "

Success

\n

Go back to rclone to continue

") + s.code <- code + } else { + fmt.Fprintf(w, "

Success

\n

Cut and paste this code into rclone: %s

", code) + } } return } @@ -244,5 +269,9 @@ func (s *authServer) Start() { func (s *authServer) Stop() { fs.Debug(nil, "Closing auth server") + if s.code != nil { + close(s.code) + s.code = nil + } _ = s.listener.Close() }