mirror of
https://github.com/taigrr/wtf
synced 2025-01-18 04:03:14 -08:00
- Simplify Google OAuth client creation - Read OAuth code via text dialog Same approach can be re-used for gspreadsheets
108 lines
2.9 KiB
Go
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
|
|
}
|