Skip to content
This repository has been archived by the owner on Aug 10, 2021. It is now read-only.

Commit

Permalink
fix!: NumericDate parsing conformance
Browse files Browse the repository at this point in the history
  • Loading branch information
dunglas committed Apr 1, 2021
1 parent 9162a5a commit ce0bdd7
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 40 deletions.
62 changes: 37 additions & 25 deletions claims.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package jwt
import (
"crypto/subtle"
"fmt"
"math"
"time"
)

Expand All @@ -17,12 +18,12 @@ type Claims interface {
// See examples for how to use this with your own claim types
type StandardClaims struct {
Audience []string `json:"aud,omitempty"`
ExpiresAt int64 `json:"exp,omitempty"`
Id string `json:"jti,omitempty"`
IssuedAt int64 `json:"iat,omitempty"`
Issuer string `json:"iss,omitempty"`
NotBefore int64 `json:"nbf,omitempty"`
Subject string `json:"sub,omitempty"`
ExpiresAt float64 `json:"exp,omitempty"`
Id string `json:"jti,omitempty"`
IssuedAt float64 `json:"iat,omitempty"`
Issuer string `json:"iss,omitempty"`
NotBefore float64 `json:"nbf,omitempty"`
Subject string `json:"sub,omitempty"`
}

// Validates time based claims "exp, iat, nbf".
Expand All @@ -31,12 +32,12 @@ type StandardClaims struct {
// be considered a valid claim.
func (c StandardClaims) Valid() error {
vErr := new(ValidationError)
now := TimeFunc().Unix()
now := TimeFunc()

// The claims below are optional, by default, so if they are set to the
// default value in Go, let's not fail the verification for them.
if c.VerifyExpiresAt(now, false) == false {
delta := time.Unix(now, 0).Sub(time.Unix(c.ExpiresAt, 0))
delta := now.Sub(parseUnixFloat(c.ExpiresAt))
vErr.Inner = fmt.Errorf("token is expired by %v", delta)
vErr.Errors |= ValidationErrorExpired
}
Expand Down Expand Up @@ -66,13 +67,13 @@ func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool {

// Compares the exp claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (c *StandardClaims) VerifyExpiresAt(cmp int64, req bool) bool {
func (c *StandardClaims) VerifyExpiresAt(cmp time.Time, req bool) bool {
return verifyExp(c.ExpiresAt, cmp, req)
}

// Compares the iat claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool) bool {
func (c *StandardClaims) VerifyIssuedAt(cmp time.Time, req bool) bool {
return verifyIat(c.IssuedAt, cmp, req)
}

Expand All @@ -84,7 +85,7 @@ func (c *StandardClaims) VerifyIssuer(cmp string, req bool) bool {

// Compares the nbf claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool) bool {
func (c *StandardClaims) VerifyNotBefore(cmp time.Time, req bool) bool {
return verifyNbf(c.NotBefore, cmp, req)
}

Expand All @@ -103,34 +104,45 @@ func verifyAud(aud []string, cmp string, required bool) bool {
return false
}

func verifyExp(exp int64, now int64, required bool) bool {
if exp == 0 {
func verifyExp(exp float64, now time.Time, required bool) bool {
if exp == 0. {
return !required
}
return now <= exp

pexp := parseUnixFloat(exp)

return pexp.Equal(now) || now.Before(pexp)
}

func verifyIat(iat int64, now int64, required bool) bool {
if iat == 0 {
func verifyIat(iat float64, now time.Time, required bool) bool {
if iat == 0. {
return !required
}
return now >= iat

piat := parseUnixFloat(iat)

return piat.Equal(now) || now.After(piat)
}

func verifyIss(iss string, cmp string, required bool) bool {
if iss == "" {
return !required
}
if subtle.ConstantTimeCompare([]byte(iss), []byte(cmp)) != 0 {
return true
} else {
return false
}

return subtle.ConstantTimeCompare([]byte(iss), []byte(cmp)) != 0
}

func verifyNbf(nbf int64, now int64, required bool) bool {
if nbf == 0 {
func verifyNbf(nbf float64, now time.Time, required bool) bool {
if nbf == 0. {
return !required
}
return now >= nbf

pnbf := parseUnixFloat(nbf)

return pnbf.Equal(now) || now.After(pnbf)
}

func parseUnixFloat(ts float64) time.Time {
int, frac := math.Modf(ts)
return time.Unix(int64(int), int64(frac*(1e9)))
}
7 changes: 4 additions & 3 deletions http_example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ import (
"bytes"
"crypto/rsa"
"fmt"
"github.com/form3tech-oss/jwt-go"
"github.com/form3tech-oss/jwt-go/request"
"io"
"io/ioutil"
"log"
Expand All @@ -17,6 +15,9 @@ import (
"net/url"
"strings"
"time"

"github.com/form3tech-oss/jwt-go"
"github.com/form3tech-oss/jwt-go/request"
)

// location of the files used for signing and verification
Expand Down Expand Up @@ -150,7 +151,7 @@ func createToken(user string) (string, error) {
&jwt.StandardClaims{
// set the expire time
// see http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-20#section-4.1.4
ExpiresAt: time.Now().Add(time.Minute * 1).Unix(),
ExpiresAt: float64(time.Now().Add(time.Minute * 1).Unix()),
},
"level1",
CustomerInfo{user, "human"},
Expand Down
21 changes: 11 additions & 10 deletions map_claims.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package jwt
import (
"encoding/json"
"errors"
"time"
// "fmt"
)

Expand All @@ -27,25 +28,25 @@ func (m MapClaims) VerifyAudience(cmp string, req bool) bool {

// Compares the exp claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (m MapClaims) VerifyExpiresAt(cmp int64, req bool) bool {
func (m MapClaims) VerifyExpiresAt(cmp time.Time, req bool) bool {
switch exp := m["exp"].(type) {
case float64:
return verifyExp(int64(exp), cmp, req)
return verifyExp(exp, cmp, req)
case json.Number:
v, _ := exp.Int64()
v, _ := exp.Float64()
return verifyExp(v, cmp, req)
}
return req == false
}

// Compares the iat claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (m MapClaims) VerifyIssuedAt(cmp int64, req bool) bool {
func (m MapClaims) VerifyIssuedAt(cmp time.Time, req bool) bool {
switch iat := m["iat"].(type) {
case float64:
return verifyIat(int64(iat), cmp, req)
return verifyIat(iat, cmp, req)
case json.Number:
v, _ := iat.Int64()
v, _ := iat.Float64()
return verifyIat(v, cmp, req)
}
return req == false
Expand All @@ -60,12 +61,12 @@ func (m MapClaims) VerifyIssuer(cmp string, req bool) bool {

// Compares the nbf claim against cmp.
// If required is false, this method will return true if the value matches or is unset
func (m MapClaims) VerifyNotBefore(cmp int64, req bool) bool {
func (m MapClaims) VerifyNotBefore(cmp time.Time, req bool) bool {
switch nbf := m["nbf"].(type) {
case float64:
return verifyNbf(int64(nbf), cmp, req)
return verifyNbf(nbf, cmp, req)
case json.Number:
v, _ := nbf.Int64()
v, _ := nbf.Float64()
return verifyNbf(v, cmp, req)
}
return req == false
Expand All @@ -77,7 +78,7 @@ func (m MapClaims) VerifyNotBefore(cmp int64, req bool) bool {
// be considered a valid claim.
func (m MapClaims) Valid() error {
vErr := new(ValidationError)
now := TimeFunc().Unix()
now := TimeFunc()

if m.VerifyExpiresAt(now, false) == false {
vErr.Inner = errors.New("Token is expired")
Expand Down
2 changes: 1 addition & 1 deletion parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ var jwtTestData = []struct {
"",
defaultKeyFunc,
&jwt.StandardClaims{
ExpiresAt: time.Now().Add(time.Second * 10).Unix(),
ExpiresAt: float64(time.Now().Add(time.Second * 10).Unix()),
},
true,
0,
Expand Down
2 changes: 1 addition & 1 deletion rsa_pss_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func TestRSAPSSSaltLengthCompatibility(t *testing.T) {
func makeToken(method jwt.SigningMethod) string {
token := jwt.NewWithClaims(method, jwt.StandardClaims{
Issuer: "example",
IssuedAt: time.Now().Unix(),
IssuedAt: float64(time.Now().Unix()),
})
privateKey := test.LoadRSAPrivateKeyFromDisk("test/sample_key")
signed, err := token.SignedString(privateKey)
Expand Down

0 comments on commit ce0bdd7

Please sign in to comment.