oauth2/google: use the metadata package, cleanups

Verified it compiles on Go 1.2 now too.

Fixes golang/oauth2#70

Change-Id: I099a86676d2464b3840f1798bbca914a202eb195
Reviewed-on: https://go-review.googlesource.com/2372
Reviewed-by: Burcu Dogan <jbd@google.com>
This commit is contained in:
Brad Fitzpatrick 2015-01-06 11:34:34 -08:00
parent 685f9f8718
commit 5361962df4

View File

@ -15,13 +15,14 @@ package google // import "golang.org/x/oauth2/google"
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"net" "strings"
"net/http"
"time" "time"
"golang.org/x/oauth2" "golang.org/x/oauth2"
"golang.org/x/oauth2/jwt" "golang.org/x/oauth2/jwt"
"google.golang.org/cloud/compute/metadata"
) )
// TODO(bradfitz,jbd): import "google.golang.org/cloud/compute/metadata" instead of // TODO(bradfitz,jbd): import "google.golang.org/cloud/compute/metadata" instead of
@ -56,12 +57,6 @@ func JWTConfigFromJSON(ctx oauth2.Context, jsonKey []byte, scope ...string) (*jw
}, nil }, nil
} }
type metaTokenRespBody struct {
AccessToken string `json:"access_token"`
ExpiresIn time.Duration `json:"expires_in"`
TokenType string `json:"token_type"`
}
// ComputeTokenSource returns a token source that fetches access tokens // ComputeTokenSource returns a token source that fetches access tokens
// from Google Compute Engine (GCE)'s metadata server. It's only valid to use // 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. // this token source if your program is running on a GCE instance.
@ -69,50 +64,40 @@ type metaTokenRespBody struct {
// Further information about retrieving access tokens from the GCE metadata // Further information about retrieving access tokens from the GCE metadata
// server can be found at https://cloud.google.com/compute/docs/authentication. // server can be found at https://cloud.google.com/compute/docs/authentication.
func ComputeTokenSource(account string) oauth2.TokenSource { func ComputeTokenSource(account string) oauth2.TokenSource {
return oauth2.ReuseTokenSource(nil, &computeSource{account: account}) return oauth2.ReuseTokenSource(nil, computeSource{account: account})
} }
type computeSource struct { type computeSource struct {
account string account string
} }
var metaClient = &http.Client{ func (cs computeSource) Token() (*oauth2.Token, error) {
Transport: &http.Transport{ if !metadata.OnGCE() {
Dial: (&net.Dialer{ return nil, errors.New("oauth2/google: can't get a token from the metadata service; not running on GCE")
Timeout: 750 * time.Millisecond, }
KeepAlive: 30 * time.Second,
}).Dial,
ResponseHeaderTimeout: 750 * time.Millisecond,
},
}
func (cs *computeSource) Token() (*oauth2.Token, error) {
acct := cs.account acct := cs.account
if acct == "" { if acct == "" {
acct = "default" acct = "default"
} }
u := "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/" + acct + "/token" tokenJSON, err := metadata.Get("instance/service-accounts/" + acct + "/token")
req, err := http.NewRequest("GET", u, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
req.Header.Add("X-Google-Metadata-Request", "True") var res struct {
resp, err := metaClient.Do(req) AccessToken string `json:"access_token"`
if err != nil { ExpiresInSec int `json:"expires_in"`
return nil, err TokenType string `json:"token_type"`
} }
defer resp.Body.Close() err = json.NewDecoder(strings.NewReader(tokenJSON)).Decode(&res)
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 { if err != nil {
return nil, err return nil, fmt.Errorf("oauth2/google: invalid token JSON from metadata: %v", err)
}
if res.ExpiresInSec == 0 || res.AccessToken == "" {
return nil, fmt.Errorf("oauth2/google: incomplete token received from metadata")
} }
return &oauth2.Token{ return &oauth2.Token{
AccessToken: tokenResp.AccessToken, AccessToken: res.AccessToken,
TokenType: tokenResp.TokenType, TokenType: res.TokenType,
Expiry: time.Now().Add(tokenResp.ExpiresIn * time.Second), Expiry: time.Now().Add(time.Duration(res.ExpiresInSec) * time.Second),
}, nil }, nil
} }