From a6f0c4b58d82134982b426ef0592b4a0fd438031 Mon Sep 17 00:00:00 2001 From: Joseph Anthony Pasquale Holsten Date: Tue, 31 May 2016 17:25:10 -0700 Subject: [PATCH] passwordcredentials: add Using PasswordCredentialsToken requires a TokenSource. This implements a Config similar to oauth2/clientcredentials for the Resource Owner Password Credentials Grant. See https://tools.ietf.org/html/rfc6749#section-4.3 for more info. Fixes #186 Change-Id: I3c6032899d6c286b84f8f24e0f6a240004f4f6c0 --- passwordcredentials/passwordcredentials.go | 99 ++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 passwordcredentials/passwordcredentials.go diff --git a/passwordcredentials/passwordcredentials.go b/passwordcredentials/passwordcredentials.go new file mode 100644 index 000000000..9580b765e --- /dev/null +++ b/passwordcredentials/passwordcredentials.go @@ -0,0 +1,99 @@ +package passwordcredentials + +import ( + "net/http" + + "golang.org/x/net/context" + "golang.org/x/oauth2" + "golang.org/x/oauth2/internal" +) + +// tokenFromInternal maps an *internal.Token struct into +// an *oauth2.Token struct. +func tokenFromInternal(t *internal.Token) *oauth2.Token { + if t == nil { + return nil + } + tk := &oauth2.Token{ + AccessToken: t.AccessToken, + TokenType: t.TokenType, + RefreshToken: t.RefreshToken, + Expiry: t.Expiry, + } + return tk.WithExtra(t.Raw) +} + +// retrieveToken takes a *Config and uses that to retrieve an *internal.Token. +// This token is then mapped from *internal.Token into an *oauth2.Token which is returned along +// with an error. +func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) { + tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v) + if err != nil { + return nil, err + } + return tokenFromInternal(tk), nil +} + +type Config struct { + // ClientID is the application's ID. + ClientID string + + // ClientSecret is the application's secret. + ClientSecret string + + // Resource owner username + Username string + + // Resource owner password + Password string + + // Endpoint contains the resource server's token endpoint + // URLs. These are constants specific to each server and are + // often available via site-specific packages, such as + // google.Endpoint or github.Endpoint. + Endpoint oauth2.Endpoint + + // Scope specifies optional requested permissions. + Scopes []string +} + +func (c *Config) Client(ctx context.Context) *http.Client { + return oauth2.NewClient(ctx, c.TokenSource(ctx)) +} + +// TokenSource returns a TokenSource that returns t until t expires, +// automatically refreshing it as necessary using the provided context and the +// client ID and client secret. +// +// Most users will use Config.Client instead. +// +// Client returns an HTTP client using the provided token. +// The token will auto-refresh as necessary. The underlying +// HTTP transport will be obtained using the provided context. +// The returned client and its Transport should not be modified. +func (c *Config) TokenSource(ctx context.Context) oauth2.TokenSource { + source := &tokenSource{ + ctx: ctx, + conf: c, + } + return oauth2.ReuseTokenSource(nil, source) +} + +type tokenSource struct { + ctx context.Context + conf *Config +} + +// Token refreshes the token by using a new client credentials request. +// tokens received this way do not include a refresh token +// Token returns a token or an error. +// Token must be safe for concurrent use by multiple goroutines. +// The returned Token must not be modified. +func (c *tokenSource) Token() (*oauth2.Token, error) { + return retrieveToken(c.ctx, c.conf, url.Values{ + "grant_type": {"password"}, + "username": {c.conf.Username}, + "password": {c.conf.Password}, + "scope": internal.CondVal(strings.Join(c.conf.Scopes, " ")), + }) +}