1
0
mirror of https://github.com/taigrr/wtf synced 2025-01-18 04:03:14 -08:00
wtf/gcal/google.go
Konstantin Vorobyev 2c07ccce46 gcal module improvements:
- Simplify Google OAuth client creation
- Read OAuth code via text dialog

Same approach can be re-used for gspreadsheets
2018-07-11 17:36:51 -07:00

108 lines
2.9 KiB
Go

package gcal
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"github.com/senorprogrammer/wtf/logger"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
)
// OAuthClientConfig describes Google oauth2 client configuration
type OAuthClientConfig struct {
SecretFile string
TokenFile string
Scope string
}
// OAuthUIHandler returns oauth2 authorization code, implements user interaction
type OAuthUIHandler = func(string, chan string)
func BuildOAuthClient(config OAuthClientConfig, handler OAuthUIHandler) (*http.Client, error) {
ctx := context.Background()
b, err := ioutil.ReadFile(config.SecretFile)
if err != nil {
logger.Log(fmt.Sprintf("Invalid secret file provided (%s): %v", config.SecretFile, err))
return nil, err
}
oauthConfig, err := google.ConfigFromJSON(b, config.Scope)
if err != nil {
logger.Log(fmt.Sprintf("Secret file is not readable as JSON (%s): %v", config.SecretFile, err))
return nil, err
}
tok, err := tokenFromFile(config.TokenFile)
if err != nil {
result := make(chan string)
go handler(oauthConfig.AuthCodeURL("state-token", oauth2.AccessTypeOffline), result)
code, ok := <-result
if !ok {
// Cancelled
return nil, err
}
tok, err = oauthConfig.Exchange(oauth2.NoContext, code)
if err != nil {
logger.Log(fmt.Sprintf("Invalid exchange code provided (%s): %v", code, err))
return nil, err
}
err = saveToken(config.TokenFile, tok)
if err != nil {
logger.Log(fmt.Sprintf("Unable to store oauth token (%s): %v", config.TokenFile, err))
}
}
return oauthConfig.Client(ctx, tok), nil
}
// CreateCodeInputDialog shows oauth2 code input dialog
func CreateCodeInputDialog(title string, widget *Widget) OAuthUIHandler {
return func(url string, result chan string) {
readInput := func() {
fmt.Printf("\r\nOAuth authorization [%s]\r\n", title)
fmt.Printf("Please open the following URL in your browser and paste authorization code below:\r\n")
fmt.Printf("%s\r\nAuthorization code: ", url)
var code string
if _, err := fmt.Scan(&code); err != nil {
logger.Log(fmt.Sprintf("Unable to read authorization code: %v", err))
close(result)
} else {
result <- code
}
}
widget.app.Suspend(readInput)
}
}
// tokenFromFile retrieves a Token from a given file path.
// It returns the retrieved Token and any read error encountered.
func tokenFromFile(file string) (*oauth2.Token, error) {
f, err := os.Open(file)
if err != nil {
return nil, err
}
t := &oauth2.Token{}
err = json.NewDecoder(f).Decode(t)
defer f.Close()
return t, err
}
// saveToken uses a file path to create a file and store the
// token in it.
func saveToken(file string, token *oauth2.Token) error {
// fmt.Printf("Saving credential file to: %s\n", file)
f, err := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
if err != nil {
return err
}
defer f.Close()
json.NewEncoder(f).Encode(token)
return nil
}