oauth2: rewrite google package, fix the broken build

Change-Id: I2753a88d7be483bdbc0cac09a1beccc4806ea4bc
Reviewed-on: https://go-review.googlesource.com/1361
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Andrew Gerrand <adg@golang.org>
This commit is contained in:
Burcu Dogan
2014-12-10 23:30:13 -08:00
committed by Brad Fitzpatrick
parent a568078818
commit 9b6b7610ad
14 changed files with 337 additions and 1026 deletions

View File

@@ -17,19 +17,41 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/url"
"time"
"golang.org/x/oauth2"
"golang.org/x/oauth2/internal"
)
var (
uriGoogleAuth, _ = url.Parse("https://accounts.google.com/o/oauth2/auth")
uriGoogleToken, _ = url.Parse("https://accounts.google.com/o/oauth2/token")
)
// Endpoint is Google's OAuth 2.0 endpoint.
var Endpoint = oauth2.Endpoint{
AuthURL: "https://accounts.google.com/o/oauth2/auth",
TokenURL: "https://accounts.google.com/o/oauth2/token",
}
// JWTTokenURL is Google's OAuth 2.0 token URL to use with the JWT flow.
const JWTTokenURL = "https://accounts.google.com/o/oauth2/token"
// JWTConfigFromJSON uses a Google Developers service account JSON key file to read
// the credentials that authorize and authenticate the requests.
// Create a service account on "Credentials" page under "APIs & Auth" for your
// project at https://console.developers.google.com to download a JSON key file.
func JWTConfigFromJSON(ctx oauth2.Context, jsonKey []byte, scope ...string) (*oauth2.JWTConfig, error) {
var key struct {
Email string `json:"client_email"`
PrivateKey string `json:"private_key"`
}
if err := json.Unmarshal(jsonKey, &key); err != nil {
return nil, err
}
return &oauth2.JWTConfig{
Email: key.Email,
PrivateKey: []byte(key.PrivateKey),
Scopes: scope,
TokenURL: JWTTokenURL,
}, nil
}
type metaTokenRespBody struct {
AccessToken string `json:"access_token"`
@@ -37,93 +59,57 @@ type metaTokenRespBody struct {
TokenType string `json:"token_type"`
}
// JWTEndpoint adds the endpoints required to complete the 2-legged service account flow.
func JWTEndpoint() oauth2.Option {
return func(opts *oauth2.Options) error {
opts.AUD = uriGoogleToken
return nil
}
// ComputeTokenSource returns a token source that fetches access tokens
// from Google Compute Engine (GCE)'s metadata server. It's only valid to use
// this token source if your program is running on a GCE instance.
// If no account is specified, "default" is used.
// Further information about retrieving access tokens from the GCE metadata
// server can be found at https://cloud.google.com/compute/docs/authentication.
func ComputeTokenSource(account string) oauth2.TokenSource {
return &computeSource{account: account}
}
// Endpoint adds the endpoints required to do the 3-legged Web server flow.
func Endpoint() oauth2.Option {
return func(opts *oauth2.Options) error {
opts.AuthURL = uriGoogleAuth
opts.TokenURL = uriGoogleToken
return nil
}
type computeSource struct {
account string
}
// ComputeEngineAccount uses the specified account to retrieve an access
// token from the Google Compute Engine's metadata server. If no user is
// provided, "default" is being used.
func ComputeEngineAccount(account string) oauth2.Option {
return func(opts *oauth2.Options) error {
if account == "" {
account = "default"
}
opts.TokenFetcherFunc = makeComputeFetcher(opts, account)
return nil
}
var metaClient = &http.Client{
Transport: &http.Transport{
Dial: (&net.Dialer{
Timeout: 750 * time.Millisecond,
KeepAlive: 30 * time.Second,
}).Dial,
ResponseHeaderTimeout: 750 * time.Millisecond,
},
}
// ServiceAccountJSONKey uses the provided Google Developers
// JSON key file to authorize the user. See the "Credentials" page under
// "APIs & Auth" for your project at https://console.developers.google.com
// to download a JSON key file.
func ServiceAccountJSONKey(filename string) oauth2.Option {
return func(opts *oauth2.Options) error {
b, err := ioutil.ReadFile(filename)
if err != nil {
return err
}
var key struct {
Email string `json:"client_email"`
PrivateKey string `json:"private_key"`
}
if err := json.Unmarshal(b, &key); err != nil {
return err
}
pk, err := internal.ParseKey([]byte(key.PrivateKey))
if err != nil {
return err
}
opts.Email = key.Email
opts.PrivateKey = pk
opts.AUD = uriGoogleToken
return nil
func (cs *computeSource) Token() (*oauth2.Token, error) {
acct := cs.account
if acct == "" {
acct = "default"
}
}
func makeComputeFetcher(opts *oauth2.Options, account string) func(*oauth2.Token) (*oauth2.Token, error) {
return func(t *oauth2.Token) (*oauth2.Token, error) {
u := "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/" + account + "/token"
req, err := http.NewRequest("GET", u, nil)
if err != nil {
return nil, err
}
req.Header.Add("X-Google-Metadata-Request", "True")
c := &http.Client{}
if opts.Client != nil {
c = opts.Client
}
resp, err := c.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode > 299 {
return nil, fmt.Errorf("oauth2: can't retrieve a token from metadata server, status code: %d", resp.StatusCode)
}
var tokenResp metaTokenRespBody
err = json.NewDecoder(resp.Body).Decode(&tokenResp)
if err != nil {
return nil, err
}
return &oauth2.Token{
AccessToken: tokenResp.AccessToken,
TokenType: tokenResp.TokenType,
Expiry: time.Now().Add(tokenResp.ExpiresIn * time.Second),
}, nil
u := "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/" + acct + "/token"
req, err := http.NewRequest("GET", u, nil)
if err != nil {
return nil, err
}
req.Header.Add("X-Google-Metadata-Request", "True")
resp, err := metaClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode > 299 {
return nil, fmt.Errorf("oauth2: can't retrieve a token from metadata server, status code: %d", resp.StatusCode)
}
var tokenResp metaTokenRespBody
err = json.NewDecoder(resp.Body).Decode(&tokenResp)
if err != nil {
return nil, err
}
return &oauth2.Token{
AccessToken: tokenResp.AccessToken,
TokenType: tokenResp.TokenType,
Expiry: time.Now().Add(tokenResp.ExpiresIn * time.Second),
}, nil
}