package token import ( "context" "encoding/json" "fmt" "net/http" "os" "time" "github.com/charmbracelet/log" "golang.org/x/oauth2" ) // Return OAuth2 Client func GetClient(config *oauth2.Config) *http.Client { // The file token.json stores the user's access and refresh tokens, and is // created automatically when the autorization flow completes for the first time tokFile := "token.json" tok, err := GetTokenFromFile(tokFile) if err != nil { tok = GetTokenFromWeb(config) SaveToken(tokFile, tok) } return config.Client(context.Background(), tok) } // Request a token from the web func GetTokenFromWeb(config *oauth2.Config) *oauth2.Token { // Simple HTTP callback handler codeCh := make(chan string) srv := &http.Server{Addr: ":4444"} http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) { code := r.URL.Query().Get("code") fmt.Fprint(w, "Authorization successful! You can close this tab.") codeCh <- code }) // Start in background go func() { srv.ListenAndServe() }() authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline, oauth2.ApprovalForce) log.Info("Go to the following link in your browser and then type the authorization code:") fmt.Println(authURL) // Get authCode from channel authCode := <-codeCh tok, err := config.Exchange(context.TODO(), authCode) if err != nil { log.Fatal("Unable to retrieve token from web", "err", err) } ctxShutdown, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() srv.Shutdown(ctxShutdown) return tok } // Retrieves a token from a file func GetTokenFromFile(file string) (*oauth2.Token, error) { f, err := os.Open(file) if err != nil { return nil, err } defer f.Close() tok := &oauth2.Token{} err = json.NewDecoder(f).Decode(tok) return tok, err } // Saves a token to a file path func SaveToken(path string, token *oauth2.Token) { log.Info("Saving credential file to", "path", path) f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { log.Fatal("Unable to cache oauth token", "err", err) } defer f.Close() json.NewEncoder(f).Encode(token) }