forked from Mirrors/oauth2
oauth2: add error type for unsuccessful token endpoint status
Allows the HTTP response and body to be extracted without parsing the error string, but keeps backwards compatibility for users who are currently doing so. Fixes golang/oauth2#173 Change-Id: Id7709da827a155299b047f0bcb74aa8f91b01e96 Reviewed-on: https://go-review.googlesource.com/84156 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
462316686f
commit
0448841f0c
|
@ -92,6 +92,9 @@ func (c *tokenSource) Token() (*oauth2.Token, error) {
|
||||||
}
|
}
|
||||||
tk, err := internal.RetrieveToken(c.ctx, c.conf.ClientID, c.conf.ClientSecret, c.conf.TokenURL, v)
|
tk, err := internal.RetrieveToken(c.ctx, c.conf.ClientID, c.conf.ClientSecret, c.conf.TokenURL, v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if rErr, ok := err.(*internal.RetrieveError); ok {
|
||||||
|
return nil, (*oauth2.RetrieveError)(rErr)
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
t := &oauth2.Token{
|
t := &oauth2.Token{
|
||||||
|
|
|
@ -200,7 +200,10 @@ func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string,
|
||||||
return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
|
return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
|
||||||
}
|
}
|
||||||
if code := r.StatusCode; code < 200 || code > 299 {
|
if code := r.StatusCode; code < 200 || code > 299 {
|
||||||
return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", r.Status, body)
|
return nil, &RetrieveError{
|
||||||
|
Response: r,
|
||||||
|
Body: body,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var token *Token
|
var token *Token
|
||||||
|
@ -249,3 +252,12 @@ func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string,
|
||||||
}
|
}
|
||||||
return token, nil
|
return token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RetrieveError struct {
|
||||||
|
Response *http.Response
|
||||||
|
Body []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RetrieveError) Error() string {
|
||||||
|
return fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", r.Response.Status, r.Body)
|
||||||
|
}
|
||||||
|
|
|
@ -419,6 +419,32 @@ func TestFetchWithNoRefreshToken(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTokenRetrieveError(t *testing.T) {
|
||||||
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.URL.String() != "/token" {
|
||||||
|
t.Errorf("Unexpected token refresh request URL, %v is found.", r.URL)
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
w.Write([]byte(`{"error": "invalid_grant"}`))
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
conf := newConf(ts.URL)
|
||||||
|
_, err := conf.Exchange(context.Background(), "exchange-code")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("got no error, expected one")
|
||||||
|
}
|
||||||
|
_, ok := err.(*RetrieveError)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("got %T error, expected *RetrieveError", err)
|
||||||
|
}
|
||||||
|
// Test error string for backwards compatibility
|
||||||
|
expected := fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", "400 Bad Request", `{"error": "invalid_grant"}`)
|
||||||
|
if errStr := err.Error(); errStr != expected {
|
||||||
|
t.Fatalf("got %#v, expected %#v", errStr, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRefreshToken_RefreshTokenReplacement(t *testing.T) {
|
func TestRefreshToken_RefreshTokenReplacement(t *testing.T) {
|
||||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
17
token.go
17
token.go
|
@ -5,6 +5,7 @@
|
||||||
package oauth2
|
package oauth2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -152,7 +153,23 @@ func tokenFromInternal(t *internal.Token) *Token {
|
||||||
func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, 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)
|
tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if rErr, ok := err.(*internal.RetrieveError); ok {
|
||||||
|
return nil, (*RetrieveError)(rErr)
|
||||||
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return tokenFromInternal(tk), nil
|
return tokenFromInternal(tk), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RetrieveError is the error returned when the token endpoint returns a
|
||||||
|
// non-2XX HTTP status code.
|
||||||
|
type RetrieveError struct {
|
||||||
|
Response *http.Response
|
||||||
|
// Body is the body that was consumed by reading Response.Body.
|
||||||
|
// It may be truncated.
|
||||||
|
Body []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RetrieveError) Error() string {
|
||||||
|
return fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", r.Response.Status, r.Body)
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue