// Token obtains an OAuth token, keeping a cached copy in file. // If the file name is not an absolute path, it is interpreted relative to the // user's home directory. func Token(file string, cfg *oauth.Config) (*oauth.Transport, error) { if !filepath.IsAbs(file) { file = filepath.Join(os.Getenv("HOME"), file) } cfg1 := *cfg cfg = &cfg1 cfg.TokenCache = oauth.CacheFile(file) tok, err := cfg.TokenCache.Token() if err == nil { return &oauth.Transport{Config: cfg, Token: tok}, nil } // Start HTTP server on localhost. l, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { var err1 error if l, err1 = net.Listen("tcp6", "[::1]:0"); err1 != nil { return nil, fmt.Errorf("oauthprompt.Token: starting HTTP server: %v", err) } } type done struct { err error code string } ch := make(chan done, 100) randState, err := randomID() if err != nil { return nil, err } cfg.RedirectURL = "http://" + l.Addr().String() + "/done" authURL := cfg1.AuthCodeURL(randState) handler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { if req.URL.Path == "/auth" { http.Redirect(w, req, authURL, 301) return } if req.URL.Path != "/done" { http.Error(w, "", 404) return } if req.FormValue("state") != randState { ch <- done{err: fmt.Errorf("oauthprompt.Token: incorrect response")} http.Error(w, "", 500) return } if code := req.FormValue("code"); code != "" { ch <- done{code: code} w.Write([]byte(success)) return } http.Error(w, "", 500) }) srv := &http.Server{Handler: handler} go srv.Serve(l) if err := openURL("http://" + l.Addr().String() + "/auth"); err != nil { l.Close() return nil, err } d := <-ch l.Close() if d.err != nil { return nil, err } tr := &oauth.Transport{Config: &cfg1} _, err = tr.Exchange(d.code) if err != nil { return nil, err } return tr, nil }