// Base code taken from: https://gist.github.com/marians/3b55318106df0e4e648158f1ffb43d38 package main import ( "context" "crypto/tls" // "fmt" "log" "net/http" "time" "encoding/json" "github.com/fatih/color" "github.com/webview/webview" "golang.org/x/oauth2" "io/ioutil" ) var ( conf *oauth2.Config ctx context.Context w webview.WebView client *http.Client ) func saveToken(tok *oauth2.Token, filename string) { j, err := json.MarshalIndent(tok, "", "\t") if err != nil { log.Fatalf("Failed to save token: %v", err) } ioutil.WriteFile(filename, j, 0777) } func loadToken(filename string) *oauth2.Token { data, err := ioutil.ReadFile(filename) if err != nil { return nil } var v = new(oauth2.Token) err = json.Unmarshal(data, &v) if err != nil { log.Fatalf("Failed to unmarshal data: %v", err) } return v } func giveCode(code string) { log.Printf("Code: %s", code) // Exchange will do the handshake to retrieve the initial access token. tok, err := conf.Exchange(ctx, code) if err != nil { log.Fatal(err) } log.Printf("Token: %s", tok) saveToken(tok, "./.token") // The HTTP Client returned by conf.Client will refresh the token as necessary. client = conf.Client(ctx, tok) _, err = client.Get("https://embed.gog.com/user/data/games") if err != nil { log.Fatal(err) } else { log.Println(color.CyanString("Authentication successful")) } w.Navigate(`data:text/html,

Success!

You are authenticated and can now return to the CLI.

`) time.Sleep(1 * time.Second) w.Terminate() } func openBrowser(url string) { w = webview.New(true) defer w.Destroy() w.SetTitle("Login") w.SetSize(600,600, webview.HintNone) w.Navigate(url) w.Bind("giveCode", giveCode) w.Init(` var params = new URLSearchParams(window.location.search); if (params.has('code')) { giveCode(params.get('code')); } `) w.Run() } type GameList struct { Owned []int `json:"owned"` } func getGameList() *GameList { res, err := client.Get("https://embed.gog.com/user/data/games") if err != nil { log.Fatalf("Failed to get game list: %v", err) } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { log.Fatalf("Failed to parse game body: %v", err) } // log.Println(string(body)) var gl = new(GameList) err = json.Unmarshal(body, gl) if err != nil { log.Fatalf("Failed to parse game list response: %v", err) } return gl } func login(url string) { log.Println(color.CyanString("You will now be taken to your browser for authentication")) time.Sleep(1 * time.Second) openBrowser(url) } func main() { ctx = context.Background() conf = &oauth2.Config{ ClientID: "46899977096215655", ClientSecret: "9d85c43b1482497dbbce61f6e4aa173a433796eeae2ca8c5f6129f2dc4de46d9", Scopes: []string{}, Endpoint: oauth2.Endpoint{ AuthURL: "https://auth.gog.com/auth", TokenURL: "https://auth.gog.com/token", }, // my own callback URL RedirectURL: "https://embed.gog.com/on_login_success?origin=client", //"http://127.0.0.1:9999/oauth/callback", } // add transport for self-signed certificate to context tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } sslcli := &http.Client{Transport: tr} ctx = context.WithValue(ctx, oauth2.HTTPClient, sslcli) // Redirect user to consent page to ask for permission // for the scopes specified above. url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline) tok := loadToken("./.token") if tok != nil { client = conf.Client(ctx, tok) _, err := client.Get("https://embed.gog.com/user/data/games") if err != nil { login(url) } else { log.Println(color.CyanString("Authentication successful")) } } else { login(url) } gl := getGameList() log.Println(gl.Owned) }