package auth import ( "context" _ "embed" "fmt" "math/rand" "net/http" "net/url" "sync" "github.com/charmbracelet/log" "github.com/pkg/browser" "golang.org/x/oauth2" ) const listen_port = 3745 var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") //go:embed credentials.json var GoogleOAuthCreds []byte //go:embed index.html var tab_html []byte func randSeq(n int) string { b := make([]rune, n) for i := range b { b[i] = letters[rand.Intn(len(letters))] } return string(b) } func fetch_google_token(config *oauth2.Config) *oauth2.Token { // request authorization var auth_code string config.RedirectURL = fmt.Sprintf("http://localhost:%d", listen_port) state_token := randSeq(16) auth_url := config.AuthCodeURL(state_token, oauth2.AccessTypeOffline) fmt.Printf("Go to the following link in your browser: \n%v", auth_url) if err := browser.OpenURL(auth_url); err != nil { log.Fatal("Unable to open browser: %v", err) } // setup http server and wait for auth code srv := &http.Server{Addr: fmt.Sprintf(":%d", listen_port)} srv_exit_done := &sync.WaitGroup{} srv_exit_done.Add(1) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { // parse query query, err := url.ParseQuery(r.URL.RawQuery) if err != nil { log.Error("Unable to parse query: %v", err) return } // ensure the state token is correct if query.Get("state") != state_token { log.Error("Invalid state token") return } // get the auth code auth_code = query.Get("code") if auth_code == "" { log.Error("No auth code provided") return } // send back a response that automatically closes the tab w.Header().Set("Content-Type", "text/html") w.WriteHeader(http.StatusOK) w.Write(tab_html) // shutdown the http server if err := srv.Shutdown(context.TODO()); err != nil { log.Fatal(err) } }) go func() { defer srv_exit_done.Done() if err := srv.ListenAndServe(); err != http.ErrServerClosed { log.Fatal("HTTP server error: %v", err) } }() srv_exit_done.Wait() // exchange auth code for token token, err := config.Exchange(context.TODO(), auth_code) if err != nil { log.Fatalf("Unable to retrieve token from web: %v", err) } return token }