Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 166 additions & 0 deletions map_claims.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package jwt

import (
"encoding/json"
"errors"
"time"
)

var (
ErrInvalidTimeValue = errors.New("token has invalid time value")
)

const (
minUnixTime = -62135596800
maxUnixTime = 253402300799
)

type MapClaims map[string]interface{}

func (m MapClaims) VerifyAudience(cmp string, req bool) bool {
var aud []string
switch v := m["aud"].(type) {
case string:
aud = []string{v}
case []string:
aud = v
case []interface{}:
for _, a := range v {
vs, ok := a.(string)
if !ok {
return false
}
aud = append(aud, vs)
}
default:
return false
}
return verifyAud(aud, cmp, req)
}

func (m MapClaims) VerifyExpiresAt(cmp int64, req bool) bool {
exp, ok := m["exp"]
if !ok {
return !req
}

expTime, err := parseTimeValue(exp)
if err != nil {
return false
}

return verifyExp(expTime, time.Unix(cmp, 0), req)
}

func (m MapClaims) VerifyIssuedAt(cmp int64, req bool) bool {
iat, ok := m["iat"]
if !ok {
return !req
}

iatTime, err := parseTimeValue(iat)
if err != nil {
return false
}

return verifyIat(iatTime, time.Unix(cmp, 0), req)
}

func (m MapClaims) VerifyNotBefore(cmp int64, req bool) bool {
nbf, ok := m["nbf"]
if !ok {
return !req
}

nbfTime, err := parseTimeValue(nbf)
if err != nil {
return false
}

return verifyNbf(nbfTime, time.Unix(cmp, 0), req)
}

func (m MapClaims) VerifyIssuer(cmp string, req bool) bool {
iss, _ := m["iss"].(string)
return verifyIss(iss, cmp, req)
}

func (m MapClaims) Valid() error {
vErr := new(ValidationError)
now := TimeFunc().Unix()

if exp, ok := m["exp"]; ok {
expTime, err := parseTimeValue(exp)
if err != nil {
vErr.Inner = ErrInvalidTimeValue
vErr.Errors |= ValidationErrorMalformed
return vErr
}
if !verifyExp(expTime, TimeFunc(), false) {
vErr.Inner = errors.New("token is expired")
vErr.Errors |= ValidationErrorExpired
}
}

if nbf, ok := m["nbf"]; ok {
nbfTime, err := parseTimeValue(nbf)
if err != nil {
vErr.Inner = ErrInvalidTimeValue
vErr.Errors |= ValidationErrorMalformed
return vErr
}
if !verifyNbf(nbfTime, TimeFunc(), false) {
vErr.Inner = errors.New("token is not valid yet")
vErr.Errors |= ValidationErrorNotValidYet
}
}

if iat, ok := m["iat"]; ok {
iatTime, err := parseTimeValue(iat)
if err != nil {
vErr.Inner = ErrInvalidTimeValue
vErr.Errors |= ValidationErrorMalformed
return vErr
}
if !verifyIat(iatTime, time.Unix(now, 0), false) {
vErr.Inner = errors.New("token used before issued")
vErr.Errors |= ValidationErrorIssuedAt
}
}

if vErr.Errors == 0 {
return nil
}

return vErr
}

func parseTimeValue(val interface{}) (time.Time, error) {
var timestamp float64

switch v := val.(type) {
case float64:
timestamp = v
case json.Number:
f, err := v.Float64()
if err != nil {
return time.Time{}, ErrInvalidTimeValue
}
timestamp = f
case int64:
timestamp = float64(v)
case int:
timestamp = float64(v)
default:
return time.Time{}, ErrInvalidTimeValue
}

if timestamp < minUnixTime || timestamp > maxUnixTime {
return time.Time{}, ErrInvalidTimeValue
}

sec := int64(timestamp)
nsec := int64((timestamp - float64(sec)) * 1e9)

return time.Unix(sec, nsec), nil
}
Loading