forked from Mirrors/oauth2
Access type and approval prompt should be set at URL generation.
This commit is contained in:
parent
cb029f4c1f
commit
97a89b3be5
|
@ -28,7 +28,7 @@ func Example_config() {
|
||||||
|
|
||||||
// Redirect user to consent page to ask for permission
|
// Redirect user to consent page to ask for permission
|
||||||
// for the scopes specified above.
|
// for the scopes specified above.
|
||||||
url := conf.AuthCodeURL("")
|
url := conf.AuthCodeURL("state", "online", "auto")
|
||||||
fmt.Printf("Visit the URL for the auth dialog: %v", url)
|
fmt.Printf("Visit the URL for the auth dialog: %v", url)
|
||||||
|
|
||||||
// Use the authorization code that is pushed to the redirect URL.
|
// Use the authorization code that is pushed to the redirect URL.
|
||||||
|
|
|
@ -36,7 +36,7 @@ func Example_webServer() {
|
||||||
|
|
||||||
// Redirect user to Google's consent page to ask for permission
|
// Redirect user to Google's consent page to ask for permission
|
||||||
// for the scopes specified above.
|
// for the scopes specified above.
|
||||||
url := config.AuthCodeURL("")
|
url := config.AuthCodeURL("state", "online", "auto")
|
||||||
fmt.Printf("Visit the URL for the auth dialog: %v", url)
|
fmt.Printf("Visit the URL for the auth dialog: %v", url)
|
||||||
|
|
||||||
// Handle the exchange code to initiate a transport
|
// Handle the exchange code to initiate a transport
|
||||||
|
|
66
oauth2.go
66
oauth2.go
|
@ -10,6 +10,7 @@ package oauth2
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"mime"
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -39,15 +40,6 @@ type TokenFetcher interface {
|
||||||
|
|
||||||
// Options represents options to provide OAuth 2.0 client credentials
|
// Options represents options to provide OAuth 2.0 client credentials
|
||||||
// and access level. A sample configuration:
|
// and access level. A sample configuration:
|
||||||
//
|
|
||||||
// opts := &oauth2.Options{
|
|
||||||
// ClientID: "<clientID>",
|
|
||||||
// ClientSecret: "ad4364309eff",
|
|
||||||
// RedirectURL: "https://homepage/oauth2callback",
|
|
||||||
// Scopes: []string{"scope1", "scope2"},
|
|
||||||
// AccessType: "offline", // retrieves a refresh token
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
// ClientID is the OAuth client identifier used when communicating with
|
// ClientID is the OAuth client identifier used when communicating with
|
||||||
// the configured OAuth provider.
|
// the configured OAuth provider.
|
||||||
|
@ -63,26 +55,6 @@ type Options struct {
|
||||||
|
|
||||||
// Scopes optionally specifies a list of requested permission scopes.
|
// Scopes optionally specifies a list of requested permission scopes.
|
||||||
Scopes []string `json:"scopes,omitempty"`
|
Scopes []string `json:"scopes,omitempty"`
|
||||||
|
|
||||||
// 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:"access_type,omitempty"`
|
|
||||||
|
|
||||||
// ApprovalPrompt indicates whether the user should be
|
|
||||||
// re-prompted for consent. If set to "auto" (default) the
|
|
||||||
// user will be prompted only if they haven't previously
|
|
||||||
// granted consent and the code can only be exchanged for an
|
|
||||||
// access token.
|
|
||||||
// If set to "force" the user will always be prompted, and the
|
|
||||||
// code can be exchanged for a refresh token.
|
|
||||||
ApprovalPrompt string `json:"-"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConfig creates a generic OAuth 2.0 configuration that talks
|
// NewConfig creates a generic OAuth 2.0 configuration that talks
|
||||||
|
@ -127,7 +99,28 @@ type Config struct {
|
||||||
|
|
||||||
// AuthCodeURL returns a URL to OAuth 2.0 provider's consent page
|
// AuthCodeURL returns a URL to OAuth 2.0 provider's consent page
|
||||||
// that asks for permissions for the required scopes explicitly.
|
// that asks for permissions for the required scopes explicitly.
|
||||||
func (c *Config) AuthCodeURL(state string) (authURL string) {
|
//
|
||||||
|
// State is a token to protect the user from CSRF attacks. You must
|
||||||
|
// always provide a non-zero string and validate that it matches the
|
||||||
|
// the state query parameter on your redirect callback.
|
||||||
|
// See http://tools.ietf.org/html/rfc6749#section-10.12 for more info.
|
||||||
|
//
|
||||||
|
// Access type is an OAuth extension that gets sent as the
|
||||||
|
// "access_type" field in the URL from AuthCodeURL.
|
||||||
|
// It may be "online" (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.
|
||||||
|
//
|
||||||
|
// Approval prompt indicates whether the user should be
|
||||||
|
// re-prompted for consent. If set to "auto" (default) the
|
||||||
|
// user will be prompted only if they haven't previously
|
||||||
|
// granted consent and the code can only be exchanged for an
|
||||||
|
// access token. If set to "force" the user will always be prompted,
|
||||||
|
// and the code can be exchanged for a refresh token.
|
||||||
|
func (c *Config) AuthCodeURL(state, accessType, prompt string) (authURL string) {
|
||||||
u := *c.authURL
|
u := *c.authURL
|
||||||
v := url.Values{
|
v := url.Values{
|
||||||
"response_type": {"code"},
|
"response_type": {"code"},
|
||||||
|
@ -135,8 +128,8 @@ func (c *Config) AuthCodeURL(state string) (authURL string) {
|
||||||
"redirect_uri": condVal(c.opts.RedirectURL),
|
"redirect_uri": condVal(c.opts.RedirectURL),
|
||||||
"scope": condVal(strings.Join(c.opts.Scopes, " ")),
|
"scope": condVal(strings.Join(c.opts.Scopes, " ")),
|
||||||
"state": condVal(state),
|
"state": condVal(state),
|
||||||
"access_type": condVal(c.opts.AccessType),
|
"access_type": condVal(accessType),
|
||||||
"approval_prompt": condVal(c.opts.ApprovalPrompt),
|
"approval_prompt": condVal(prompt),
|
||||||
}
|
}
|
||||||
q := v.Encode()
|
q := v.Encode()
|
||||||
if u.RawQuery == "" {
|
if u.RawQuery == "" {
|
||||||
|
@ -210,9 +203,12 @@ func (c *Config) retrieveToken(v url.Values) (*Token, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer r.Body.Close()
|
defer r.Body.Close()
|
||||||
if r.StatusCode != 200 {
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
// TODO(jbd): Add status code or error message
|
if err != nil {
|
||||||
return nil, errors.New("oauth2: can't retrieve a new token")
|
return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
|
||||||
|
}
|
||||||
|
if c := r.StatusCode; c < 200 || c > 299 {
|
||||||
|
return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", r.Status, body)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := &tokenRespBody{}
|
resp := &tokenRespBody{}
|
||||||
|
|
|
@ -29,15 +29,13 @@ func newTestConf(url string) *Config {
|
||||||
"scope1",
|
"scope1",
|
||||||
"scope2",
|
"scope2",
|
||||||
},
|
},
|
||||||
AccessType: "offline",
|
|
||||||
ApprovalPrompt: "force",
|
|
||||||
}, url+"/auth", url+"/token")
|
}, url+"/auth", url+"/token")
|
||||||
return conf
|
return conf
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAuthCodeURL(t *testing.T) {
|
func TestAuthCodeURL(t *testing.T) {
|
||||||
conf := newTestConf("server")
|
conf := newTestConf("server")
|
||||||
url := conf.AuthCodeURL("foo")
|
url := conf.AuthCodeURL("foo", "offline", "force")
|
||||||
if url != "server/auth?access_type=offline&approval_prompt=force&client_id=CLIENT_ID&redirect_uri=REDIRECT_URL&response_type=code&scope=scope1+scope2&state=foo" {
|
if url != "server/auth?access_type=offline&approval_prompt=force&client_id=CLIENT_ID&redirect_uri=REDIRECT_URL&response_type=code&scope=scope1+scope2&state=foo" {
|
||||||
t.Fatalf("Auth code URL doesn't match the expected, found: %v", url)
|
t.Fatalf("Auth code URL doesn't match the expected, found: %v", url)
|
||||||
}
|
}
|
||||||
|
@ -47,7 +45,7 @@ func TestAuthCodeURL_Optional(t *testing.T) {
|
||||||
conf, _ := NewConfig(&Options{
|
conf, _ := NewConfig(&Options{
|
||||||
ClientID: "CLIENT_ID",
|
ClientID: "CLIENT_ID",
|
||||||
}, "auth-url", "token-url")
|
}, "auth-url", "token-url")
|
||||||
url := conf.AuthCodeURL("")
|
url := conf.AuthCodeURL("", "", "")
|
||||||
if url != "auth-url?client_id=CLIENT_ID&response_type=code" {
|
if url != "auth-url?client_id=CLIENT_ID&response_type=code" {
|
||||||
t.Fatalf("Auth code URL doesn't match the expected, found: %v", url)
|
t.Fatalf("Auth code URL doesn't match the expected, found: %v", url)
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ type Token struct {
|
||||||
// initialized as needed.
|
// initialized as needed.
|
||||||
Extra map[string]string `json:"extra,omitempty"`
|
Extra map[string]string `json:"extra,omitempty"`
|
||||||
|
|
||||||
// JWT related fields
|
// Subject is the user to impersonate.
|
||||||
Subject string `json:"subject,omitempty"`
|
Subject string `json:"subject,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue