Files
streamctl/pkg/oauthhandler/oauth2_handler.go
2024-02-12 13:38:01 +00:00

108 lines
2.5 KiB
Go

package oauthhandler
import (
"context"
"fmt"
"log"
"net"
"net/http"
"os/exec"
"runtime"
"github.com/facebookincubator/go-belt/tool/logger"
)
type OAuth2Handler struct {
authURL string
exchangeFn func(code string) error
receiverAddr string
}
func NewOAuth2Handler(
authURL string,
exchangeFn func(code string) error,
receiverAddr string,
) *OAuth2Handler {
return &OAuth2Handler{
authURL: authURL,
exchangeFn: exchangeFn,
receiverAddr: receiverAddr,
}
}
// it is guaranteed exchangeFn was called if error is nil.
func (h *OAuth2Handler) Handle(ctx context.Context) error {
if h.receiverAddr != "" {
err := h.handleViaBrowser()
if err == nil {
return nil
}
logger.Errorf(ctx, "unable to authenticate automatically: %v", err)
}
return h.handleViaCLI()
}
func (h *OAuth2Handler) handleViaCLI() error {
fmt.Printf(
"It is required to get an oauth2 token. "+
"Please open the link below in the browser:\n\n\t%s\n\n",
h.authURL,
)
fmt.Printf("Enter the code: ")
var code string
if _, err := fmt.Scan(&code); err != nil {
log.Fatalf("Unable to read authorization code %v", err)
}
return h.exchangeFn(code)
}
func (h *OAuth2Handler) handleViaBrowser() error {
codeCh, err := h.newCodeReceiver()
if err != nil {
return err
}
err = launchBrowser(h.authURL)
if err != nil {
return err
}
fmt.Printf("Your browser has been launched (URL: %s).\nPlease approve the permissions.\n", h.authURL)
// Wait for the web server to get the code.
code := <-codeCh
return h.exchangeFn(code)
}
func (h *OAuth2Handler) newCodeReceiver() (codeCh chan string, err error) {
// this function was mostly borrowed from https://developers.google.com/youtube/v3/code_samples/go#authorize_a_request
listener, err := net.Listen("tcp", h.receiverAddr)
if err != nil {
return nil, err
}
codeCh = make(chan string)
go http.Serve(listener, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
code := r.FormValue("code")
codeCh <- code
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
}
func launchBrowser(url string) error {
switch runtime.GOOS {
case "darwin":
return exec.Command("open", url).Start()
case "linux":
return exec.Command("xdg-open", url).Start()
case "windows":
return exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start()
}
return fmt.Errorf("unsupported platform: <%s>", runtime.GOOS)
}