diff --git a/internal/token.go b/internal/token.go index 0f75a18..955d5a0 100644 --- a/internal/token.go +++ b/internal/token.go @@ -78,6 +78,9 @@ func (e *tokenJSON) expiry() (t time.Time) { type expirationTime int32 func (e *expirationTime) UnmarshalJSON(b []byte) error { + if len(b) == 0 || string(b) == "null" { + return nil + } var n json.Number err := json.Unmarshal(b, &n) if err != nil { @@ -257,7 +260,7 @@ func doTokenRoundTrip(ctx context.Context, req *http.Request) (*Token, error) { Raw: vals, } e := vals.Get("expires_in") - if e == "" { + if e == "" || e == "null" { // TODO(jbd): Facebook's OAuth2 implementation is broken and // returns expires_in field in expires. Remove the fallback to expires, // when Facebook fixes their implementation. diff --git a/oauth2_test.go b/oauth2_test.go index a059b8b..b76eaae 100644 --- a/oauth2_test.go +++ b/oauth2_test.go @@ -275,17 +275,22 @@ const day = 24 * time.Hour func TestExchangeRequest_JSONResponse_Expiry(t *testing.T) { seconds := int32(day.Seconds()) for _, c := range []struct { + name string expires string want bool }{ - {fmt.Sprintf(`"expires_in": %d`, seconds), true}, - {fmt.Sprintf(`"expires_in": "%d"`, seconds), true}, // PayPal case - {fmt.Sprintf(`"expires": %d`, seconds), true}, // Facebook case - {`"expires": false`, false}, // wrong type - {`"expires": {}`, false}, // wrong type - {`"expires": "zzz"`, false}, // wrong value + {"normal", fmt.Sprintf(`"expires_in": %d`, seconds), true}, + {"paypal", fmt.Sprintf(`"expires_in": "%d"`, seconds), true}, + {"facebook", fmt.Sprintf(`"expires": %d`, seconds), true}, + {"issue_239", fmt.Sprintf(`"expires_in": null, "expires": %d`, seconds), true}, + + {"wrong_type", `"expires": false`, false}, + {"wrong_type2", `"expires": {}`, false}, + {"wrong_value", `"expires": "zzz"`, false}, } { - testExchangeRequest_JSONResponse_expiry(t, c.expires, c.want) + t.Run(c.name, func(t *testing.T) { + testExchangeRequest_JSONResponse_expiry(t, c.expires, c.want) + }) } }