forked from Mirrors/oauth2
Docs, code cleanups.
This commit is contained in:
parent
eb7270d354
commit
a9dc52b3d3
29
oauth2.go
29
oauth2.go
|
@ -14,6 +14,7 @@ import (
|
||||||
"mime"
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -22,7 +23,7 @@ type tokenRespBody struct {
|
||||||
AccessToken string `json:"access_token"`
|
AccessToken string `json:"access_token"`
|
||||||
TokenType string `json:"token_type"`
|
TokenType string `json:"token_type"`
|
||||||
RefreshToken string `json:"refresh_token"`
|
RefreshToken string `json:"refresh_token"`
|
||||||
ExpiresIn time.Duration `json:"expires_in"`
|
ExpiresIn int64 `json:"expires_in"` // in seconds
|
||||||
IdToken string `json:"id_token"`
|
IdToken string `json:"id_token"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +64,15 @@ type Options struct {
|
||||||
// Optional, identifies the level of access being requested.
|
// Optional, identifies the level of access being requested.
|
||||||
Scopes []string `json:"scopes"`
|
Scopes []string `json:"scopes"`
|
||||||
|
|
||||||
// Optional, "online" (default) or "offline", no refresh token if "online"
|
// AccessType is an OAuth extension that gets sent as the
|
||||||
|
// "access_type" field in the URL from AuthCodeURL.
|
||||||
|
// See https://developers.google.com/accounts/docs/OAuth2WebServer.
|
||||||
|
// It may be "online" (the default) or "offline".
|
||||||
|
// If your application needs to refresh access tokens when the
|
||||||
|
// user is not present at the browser, then use offline. This
|
||||||
|
// will result in your application obtaining a refresh token
|
||||||
|
// the first time your application exchanges an authorization
|
||||||
|
// code for a user.
|
||||||
AccessType string `json:"omit"`
|
AccessType string `json:"omit"`
|
||||||
|
|
||||||
// ApprovalPrompt indicates whether the user should be
|
// ApprovalPrompt indicates whether the user should be
|
||||||
|
@ -188,6 +197,7 @@ func (c *Config) Exchange(exchangeCode string) (*Token, error) {
|
||||||
token := &Token{}
|
token := &Token{}
|
||||||
vals := url.Values{
|
vals := url.Values{
|
||||||
"grant_type": {"authorization_code"},
|
"grant_type": {"authorization_code"},
|
||||||
|
"client_secret": {c.opts.ClientSecret},
|
||||||
"code": {exchangeCode},
|
"code": {exchangeCode},
|
||||||
}
|
}
|
||||||
if len(c.opts.Scopes) != 0 {
|
if len(c.opts.Scopes) != 0 {
|
||||||
|
@ -203,9 +213,15 @@ func (c *Config) Exchange(exchangeCode string) (*Token, error) {
|
||||||
return token, nil
|
return token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updateToken mutates both tok and v.
|
||||||
func (c *Config) updateToken(tok *Token, v url.Values) error {
|
func (c *Config) updateToken(tok *Token, v url.Values) error {
|
||||||
v.Set("client_id", c.opts.ClientID)
|
v.Set("client_id", c.opts.ClientID)
|
||||||
v.Set("client_secret", c.opts.ClientSecret)
|
// Note that we're not setting v's client_secret to t.ClientSecret, due
|
||||||
|
// to https://code.google.com/p/goauth2/issues/detail?id=31
|
||||||
|
// Reddit only accepts client_secret in Authorization header.
|
||||||
|
// Dropbox accepts either, but not both.
|
||||||
|
// The spec requires servers to always support the Authorization header,
|
||||||
|
// so that's all we use.
|
||||||
r, err := http.DefaultClient.PostForm(c.tokenURL.String(), v)
|
r, err := http.DefaultClient.PostForm(c.tokenURL.String(), v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -231,15 +247,12 @@ func (c *Config) updateToken(tok *Token, v url.Values) error {
|
||||||
resp.AccessToken = vals.Get("access_token")
|
resp.AccessToken = vals.Get("access_token")
|
||||||
resp.TokenType = vals.Get("token_type")
|
resp.TokenType = vals.Get("token_type")
|
||||||
resp.RefreshToken = vals.Get("refresh_token")
|
resp.RefreshToken = vals.Get("refresh_token")
|
||||||
resp.ExpiresIn, _ = time.ParseDuration(vals.Get("expires_in") + "s")
|
resp.ExpiresIn, _ = strconv.ParseInt(vals.Get("expires_in"), 10, 64)
|
||||||
resp.IdToken = vals.Get("id_token")
|
resp.IdToken = vals.Get("id_token")
|
||||||
default:
|
default:
|
||||||
if err = json.NewDecoder(r.Body).Decode(&resp); err != nil {
|
if err = json.NewDecoder(r.Body).Decode(&resp); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// The JSON parser treats the unitless ExpiresIn like 'ns' instead of 's' as above,
|
|
||||||
// so compensate here.
|
|
||||||
resp.ExpiresIn *= time.Second
|
|
||||||
}
|
}
|
||||||
tok.AccessToken = resp.AccessToken
|
tok.AccessToken = resp.AccessToken
|
||||||
tok.TokenType = resp.TokenType
|
tok.TokenType = resp.TokenType
|
||||||
|
@ -250,7 +263,7 @@ func (c *Config) updateToken(tok *Token, v url.Values) error {
|
||||||
if resp.ExpiresIn == 0 {
|
if resp.ExpiresIn == 0 {
|
||||||
tok.Expiry = time.Time{}
|
tok.Expiry = time.Time{}
|
||||||
} else {
|
} else {
|
||||||
tok.Expiry = time.Now().Add(resp.ExpiresIn)
|
tok.Expiry = time.Now().Add(time.Duration(resp.ExpiresIn) * time.Second)
|
||||||
}
|
}
|
||||||
if resp.IdToken != "" {
|
if resp.IdToken != "" {
|
||||||
if tok.Extra == nil {
|
if tok.Extra == nil {
|
||||||
|
|
|
@ -30,6 +30,10 @@ type Token struct {
|
||||||
// The remaining lifetime of the access token.
|
// The remaining lifetime of the access token.
|
||||||
Expiry time.Time `json:"expiry,omitempty"`
|
Expiry time.Time `json:"expiry,omitempty"`
|
||||||
|
|
||||||
|
// Extra optionally contains extra metadata from the server
|
||||||
|
// when updating a token. The only current key that may be
|
||||||
|
// populated is "id_token". It may be nil and will be
|
||||||
|
// initialized as needed.
|
||||||
Extra map[string]string `json:"extra,omitempty"`
|
Extra map[string]string `json:"extra,omitempty"`
|
||||||
|
|
||||||
// JWT related fields
|
// JWT related fields
|
||||||
|
|
Loading…
Reference in New Issue