100 lines
2.2 KiB
Go
100 lines
2.2 KiB
Go
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
|
|
}
|