-
Notifications
You must be signed in to change notification settings - Fork 184
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor(auth): improve authentication flow for oauth 2.0 with local webserver #325
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package gateclient | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
"net" | ||
"net/http" | ||
"os/exec" | ||
"runtime" | ||
|
||
"golang.org/x/oauth2" | ||
) | ||
|
||
const localWebServer = "localhost:8085" | ||
|
||
func startWebServer() (codeCh chan string, err error) { | ||
listener, err := net.Listen("tcp", localWebServer) | ||
if err != nil { | ||
return nil, err | ||
} | ||
codeCh = make(chan string) | ||
|
||
go http.Serve(listener, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
// TODO: add handle of `error` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is commented but new in this PR. Can you give an example of what |
||
//error := r.FormValue("error") | ||
code := r.FormValue("code") | ||
codeCh <- code // send code to OAuth flow | ||
listener.Close() | ||
w.Header().Set("Content-Type", "text/plain") | ||
fmt.Fprintf(w, "Received code: %v\r\nYou can now safely close this browser window.", code) | ||
})) | ||
|
||
return codeCh, nil | ||
} | ||
|
||
// getTokenFromWeb uses Config to request a Token. | ||
// It returns the retrieved Token. | ||
func getTokenFromWeb(output func(string), config *oauth2.Config, authURL string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error) { | ||
codeCh, err := startWebServer() | ||
if err != nil { | ||
output("Unable to start a web server.") | ||
return nil, err | ||
} | ||
|
||
err = openURL(authURL) | ||
if err != nil { | ||
log.Fatalf("Unable to open authorization URL in web server: %v", err) | ||
} else { | ||
output("Your browser has been opened to an authorization URL.\n" + | ||
" This program will resume once authorization has been provided.\n") | ||
output(authURL) | ||
} | ||
|
||
// Wait for the web server to get the code. | ||
code := <-codeCh | ||
return exchangeToken(config, code, opts...) | ||
} | ||
|
||
// Exchange the authorization code for an access token | ||
func exchangeToken(config *oauth2.Config, code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error) { | ||
tok, err := config.Exchange(context.Background(), code, opts...) | ||
if err != nil { | ||
return nil, fmt.Errorf("Unable to retrieve token %v", err) | ||
} | ||
return tok, nil | ||
} | ||
|
||
// openURL opens a browser window to the specified location. | ||
// This code originally appeared at: | ||
// http://stackoverflow.com/questions/10377243/how-can-i-launch-a-process-that-is-not-a-file-in-go | ||
func openURL(url string) error { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using a local web server or prompt is optional in the linked code and this seems quite robust. If local web server doesn't work then use the prompt. https://github.com/youtube/api-samples/blob/master/go/oauth2.go I'm not sure what the failure modes are for the various If an error is returned we bail out at L48 with I wonder if we might want to do something like: WDYT? |
||
var err error | ||
switch runtime.GOOS { | ||
case "linux": | ||
err = exec.Command("xdg-open", url).Start() | ||
case "windows": | ||
err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start() | ||
case "darwin": | ||
err = exec.Command("open", url).Start() | ||
default: | ||
err = fmt.Errorf("Cannot open URL %s on this platform", url) | ||
} | ||
return err | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a test for this please? - see: https://pkg.go.dev/net/http/httptest
We can validate:
code
FormValue is supplied in test and plumbed through to codeCherror
FormValue (per next comment - if part of this PR).