perkeep/third_party/github.com/tgulacsi/picago/auth.go

117 lines
3.7 KiB
Go

// Copyright 2014 Tamás Gulácsi. All rights reserved.
// Use of this source code is governed by an Apache 2.0
// license that can be found in the LICENSE file.
package picago
import (
"errors"
"fmt"
"net"
"net/http"
"camlistore.org/third_party/code.google.com/p/goauth2/oauth"
)
const picasaScope = "https://picasaweb.google.com/data/"
var ErrCodeNeeded = errors.New("Authorization code is needed")
// Authorize authorizes using OAuth2
// the ID and secret strings can be acquired from Google for the application
// https://developers.google.com/accounts/docs/OAuth2#basicsteps
func Authorize(ID, secret string) error {
return errors.New("Not implemented")
}
// NewClient returns an authorized http.Client usable for requests,
// caching tokens in the given file.
func NewClient(id, secret, code, tokenCacheFilename string) (*http.Client, error) {
return NewClientCache(id, secret, code, oauth.CacheFile(tokenCacheFilename))
}
// For redirect_uri, see https://developers.google.com/accounts/docs/OAuth2InstalledApp#choosingredirecturi .
//
// NewClientCache returns an authorized http.Client with the given oauth.Cache implementation
func NewClientCache(id, secret, code string, cache oauth.Cache) (*http.Client, error) {
transport, err := NewTransport(id, secret, cache)
if err != nil {
return nil, err
}
// Try to pull the token from the cache; if this fails, we need to get one.
token, err := transport.Config.TokenCache.Token()
if err == nil {
transport.Token = token
} else {
if id == "" || secret == "" {
return nil, errors.New("token cache is empty, thus client ID and secret is needed!")
}
if code == "" {
l, err := getListener()
if err != nil {
return nil, err
}
donech := make(chan struct{}, 1)
transport.Config.RedirectURL = "http://" + l.Addr().String()
// Get an authorization code from the data provider.
// ("Please ask the user if I can access this resource.")
url := transport.Config.AuthCodeURL("picago")
fmt.Println("Visit this URL to get a code, then run again with code=YOUR_CODE\n")
fmt.Println(url)
srv := &http.Server{Handler: NewAuthorizeHandler(transport, donech)}
go srv.Serve(l)
<-donech
l.Close()
if transport.Token == nil {
return nil, ErrCodeNeeded
}
}
if transport.Token == nil {
// Exchange the authorization code for an access token.
// ("Here's the code you gave the user, now give me a token!")
transport.Token, err = transport.Exchange(code)
if err != nil {
return nil, fmt.Errorf("Exchange: %v", err)
}
}
}
return &http.Client{Transport: transport}, nil
}
func NewTransport(id, secret string, cache oauth.Cache) (*oauth.Transport, error) {
if id == "" || secret == "" {
return nil, errors.New("Client ID and secret is needed!")
}
config := &oauth.Config{
ClientId: id,
ClientSecret: secret,
AuthURL: "https://accounts.google.com/o/oauth2/auth",
TokenURL: "https://accounts.google.com/o/oauth2/token",
Scope: picasaScope,
TokenCache: cache,
}
return &oauth.Transport{Config: config}, nil
}
// NewAuthorizeHandler returns a http.HandlerFunc which will set the Token of
// the given oauth.Transport and send a struct{} on the donech on success.
func NewAuthorizeHandler(transport *oauth.Transport, donech chan<- struct{}) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
token, err := transport.Exchange(r.FormValue("code"))
if err != nil {
http.Error(w, fmt.Sprintf("error exchanging code: %v", err), http.StatusBadRequest)
return
}
transport.Token = token
// The Transport now has a valid Token.
donech <- struct{}{}
}
}
func getListener() (*net.TCPListener, error) {
return net.ListenTCP("tcp", &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 0})
}