forked from golang/oauth2
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 golang#186 Change-Id: I3c6032899d6c286b84f8f24e0f6a240004f4f6c0
- Loading branch information
1 parent
c406a4c
commit a6f0c4b
Showing
1 changed file
with
99 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, " ")), | ||
}) | ||
} |