diff --git a/google/google.go b/google/google.go index 0be3e36..03113bb 100644 --- a/google/google.go +++ b/google/google.go @@ -75,21 +75,35 @@ const ( uriGoogleToken = "https://accounts.google.com/o/oauth2/token" ) +// ComputeEngineConfig represents a OAuth 2.0 consumer client +// running on Google Compute Engine. +type ComputeEngineConfig struct{} + // NewConfig creates a new OAuth2 config that uses Google // endpoints. func NewConfig(opts *oauth2.Options) (oauth2.Config, error) { return oauth2.NewConfig(opts, uriGoogleAuth, uriGoogleToken) } -// NewComputeEngineConfig creates a new config that can fetch tokens -// from Google Compute Engine instance's metaserver. -func NewComputeEngineConfig() (oauth2.Config, error) { - // Should fetch an access token from the meta server. - panic("not yet implemented") -} - // NewServiceAccountConfig creates a new JWT config that can // fetch Bearer JWT tokens from Google endpoints. func NewServiceAccountConfig(opts *oauth2.JWTOptions) (oauth2.JWTConfig, error) { return oauth2.NewJWTConfig(opts, uriGoogleToken) } + +// NewComputeEngineConfig creates a new config that can fetch tokens +// from Google Compute Engine instance's metaserver. +func NewComputeEngineConfig() (*ComputeEngineConfig, error) { + // Should fetch an access token from the meta server. + return &ComputeEngineConfig{}, nil +} + +// NewTransport creates an authorized transport. +func (c *ComputeEngineConfig) NewTransport() (oauth2.Transport, error) { + return oauth2.NewAuthorizedTransport(c, nil), nil +} + +// FetchToken retrieves a new access token via metadata server. +func (c *ComputeEngineConfig) FetchToken(existing *oauth2.Token) (*oauth2.Token, error) { + panic("not yet implemented") +} diff --git a/jwt.go b/jwt.go index 268dc0b..ec3c01d 100644 --- a/jwt.go +++ b/jwt.go @@ -56,18 +56,18 @@ type jwtConfig struct { // NewTransport creates a transport that is authorize with the // parent JWT configuration. func (c *jwtConfig) NewTransport() (Transport, error) { - return &authorizedTransport{fetcher: c, token: &Token{}}, nil + return NewAuthorizedTransport(c, &Token{}), nil } // NewTransportWithUser creates a transport that is authorized by // the client and impersonates the specified user. func (c *jwtConfig) NewTransportWithUser(user string) (Transport, error) { - return &authorizedTransport{fetcher: c, token: &Token{Subject: user}}, nil + return NewAuthorizedTransport(c, &Token{Subject: user}), nil } // fetchToken retrieves a new access token and updates the existing token // with the newly fetched credentials. -func (c *jwtConfig) fetchToken(existing *Token) (token *Token, err error) { +func (c *jwtConfig) FetchToken(existing *Token) (token *Token, err error) { if existing == nil { existing = &Token{} diff --git a/oauth2.go b/oauth2.go index a4cac30..b492ff6 100644 --- a/oauth2.go +++ b/oauth2.go @@ -52,14 +52,14 @@ type tokenRespBody struct { IdToken string `json:"id_token"` } -// tokenFetcher refreshes or fetches a new access token from the +// TokenFetcher refreshes or fetches a new access token from the // provider. It should return an error if it's not capable of // retrieving a token. -type tokenFetcher interface { - // fetchToken retrieves a new access token for the provider. +type TokenFetcher interface { + // FetchToken retrieves a new access token for the provider. // If the implementation doesn't know how to retrieve a new token, // it returns an error. - fetchToken(existing *Token) (*Token, error) + FetchToken(existing *Token) (*Token, error) } // Options represents options to provide OAuth 2.0 client credentials @@ -120,9 +120,6 @@ type Config interface { // Exchange ecxhanges the code with the provider to retrieve // a new access token. Exchange(exchangeCode string) (*Token, error) - - // TODO(jbd): Token fetcher strategy should be settable - // from external packages. } // Config represents an OAuth 2.0 provider and client options to @@ -136,9 +133,6 @@ type JWTConfig interface { // to be authorized with OAuth 2.0 JWT Bearer flow and // impersonates the provided user. NewTransportWithUser(user string) (Transport, error) - - // TODO(jbd): Token fetcher strategy should be settable - // from external packages. } // NewConfig creates a generic OAuth 2.0 configuration that talks @@ -155,7 +149,7 @@ func NewConfig(opts *Options, authURL, tokenURL string) (Config, error) { return conf, nil } -// config represent the configuration of an OAuth 2.0 consumer client. +// config represents the configuration of an OAuth 2.0 consumer client. type config struct { opts *Options // AuthURL is the URL the user will be directed to @@ -199,7 +193,7 @@ func (c *config) AuthCodeURL(state string) (authURL string, err error) { // t.SetToken(validToken) // func (c *config) NewTransport() (Transport, error) { - return &authorizedTransport{fetcher: c}, nil + return NewAuthorizedTransport(c, nil), nil } // NewTransportWithCode exchanges the OAuth 2.0 exchange code with @@ -211,7 +205,7 @@ func (c *config) NewTransportWithCode(exchangeCode string) (Transport, error) { if err != nil { return nil, err } - return &authorizedTransport{fetcher: c, token: token}, nil + return NewAuthorizedTransport(c, token), nil } // Exchange exchanges the exchange code with the OAuth 2.0 provider @@ -230,10 +224,10 @@ func (c *config) Exchange(exchangeCode string) (*Token, error) { return token, nil } -// fetchToken retrieves a new access token and updates the existing token +// FetchToken retrieves a new access token and updates the existing token // with the newly fetched credentials. If existing token doesn't // contain a refresh token, it returns an error. -func (c *config) fetchToken(existing *Token) (*Token, error) { +func (c *config) FetchToken(existing *Token) (*Token, error) { if existing == nil || existing.RefreshToken == "" { return nil, errors.New("cannot fetch access token without refresh token.") } @@ -309,7 +303,7 @@ func (c *config) updateToken(tok *Token, v url.Values) error { tok.AccessToken = resp.AccessToken tok.TokenType = resp.TokenType // Don't overwrite `RefreshToken` with an empty value - if resp.RefreshToken == "" { + if resp.RefreshToken != "" { tok.RefreshToken = resp.RefreshToken } if resp.ExpiresIn == 0 { diff --git a/transport.go b/transport.go index f7299c3..d381a79 100644 --- a/transport.go +++ b/transport.go @@ -72,13 +72,20 @@ type Transport interface { } type authorizedTransport struct { - fetcher tokenFetcher + fetcher TokenFetcher token *Token // Mutex to protect token during auto refreshments. mu sync.RWMutex } +// NewAuthorizedTransport creates a tranport that uses the provided +// token fetcher to retrieve new tokens if there is no access token +// provided or it is expired. +func NewAuthorizedTransport(fetcher TokenFetcher, token *Token) Transport { + return &authorizedTransport{fetcher: fetcher, token: token} +} + // RoundTrip authorizes the request with the existing token. // If token is expired, tries to refresh/fetch a new token. func (t *authorizedTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) { @@ -142,7 +149,7 @@ func (t *authorizedTransport) RefreshToken() error { t.mu.Lock() defer t.mu.Unlock() - token, err := t.fetcher.fetchToken(t.token) + token, err := t.fetcher.FetchToken(t.token) if err != nil { return err }