oauth2: fix expires_in for PayPal

PayPal returns "expires_in" token field as string, not integer.
So, current implementation cannot unmarshal json of tokenJSON due type mismatch.
This patch fixes the issue declaring field as interface{} in tokenJSON and performing type switch in "func (e *tokenJSON) expiry()".

Related to issue #41.

Change-Id: I69301e08c8a56fca049ca47906e32528cd22aef9
Reviewed-on: https://go-review.googlesource.com/6924
Reviewed-by: Andrew Gerrand <adg@golang.org>
This commit is contained in:
Nikolay Turpitko 2015-03-06 14:54:39 +06:00 committed by Andrew Gerrand
parent 54a4310f85
commit 6d4eed4495
2 changed files with 75 additions and 5 deletions

View File

@ -377,8 +377,8 @@ type tokenJSON struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
RefreshToken string `json:"refresh_token"`
ExpiresIn int32 `json:"expires_in"`
Expires int32 `json:"expires"` // broken Facebook spelling of expires_in
ExpiresIn expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number
Expires expirationTime `json:"expires"` // broken Facebook spelling of expires_in
}
func (e *tokenJSON) expiry() (t time.Time) {
@ -391,6 +391,22 @@ func (e *tokenJSON) expiry() (t time.Time) {
return
}
type expirationTime int32
func (e *expirationTime) UnmarshalJSON(b []byte) error {
var n json.Number
err := json.Unmarshal(b, &n)
if err != nil {
return err
}
i, err := n.Int64()
if err != nil {
return err
}
*e = expirationTime(i)
return nil
}
func condVal(v string) []string {
if v == "" {
return nil

View File

@ -5,12 +5,16 @@
package oauth2
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"reflect"
"strconv"
"testing"
"time"
"golang.org/x/net/context"
)
@ -159,6 +163,56 @@ func TestExchangeRequest_JSONResponse(t *testing.T) {
}
}
const day = 24 * time.Hour
func TestExchangeRequest_JSONResponse_Expiry(t *testing.T) {
seconds := int32(day.Seconds())
jsonNumberType := reflect.TypeOf(json.Number("0"))
for _, c := range []struct {
expires string
expect error
}{
{fmt.Sprintf(`"expires_in": %d`, seconds), nil},
{fmt.Sprintf(`"expires_in": "%d"`, seconds), nil}, // PayPal case
{fmt.Sprintf(`"expires": %d`, seconds), nil}, // Facebook case
{`"expires": false`, &json.UnmarshalTypeError{"bool", jsonNumberType}}, // wrong type
{`"expires": {}`, &json.UnmarshalTypeError{"object", jsonNumberType}}, // wrong type
{`"expires": "zzz"`, &strconv.NumError{"ParseInt", "zzz", strconv.ErrSyntax}}, // wrong value
} {
testExchangeRequest_JSONResponse_expiry(t, c.expires, c.expect)
}
}
func testExchangeRequest_JSONResponse_expiry(t *testing.T, exp string, expect error) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(fmt.Sprintf(`{"access_token": "90d", "scope": "user", "token_type": "bearer", %s}`, exp)))
}))
defer ts.Close()
conf := newConf(ts.URL)
t1 := time.Now().Add(day)
tok, err := conf.Exchange(NoContext, "exchange-code")
t2 := time.Now().Add(day)
if err == nil && expect != nil {
t.Errorf("Incorrect state, conf.Exchange() should return an error: %v", expect)
} else if err != nil {
if reflect.DeepEqual(err, expect) {
t.Logf("Expected error: %v", err)
return
} else {
t.Error(err)
}
}
if !tok.Valid() {
t.Fatalf("Token invalid. Got: %#v", tok)
}
expiry := tok.Expiry
if expiry.Before(t1) || expiry.After(t2) {
t.Errorf("Unexpected value for Expiry: %v (shold be between %v and %v)", expiry, t1, t2)
}
}
func TestExchangeRequest_BadResponse(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")