claims.go (3964B)
1 package jwt 2 3 import ( 4 "crypto/subtle" 5 "fmt" 6 "time" 7 ) 8 9 // For a type to be a Claims object, it must just have a Valid method that determines 10 // if the token is invalid for any supported reason 11 type Claims interface { 12 Valid() error 13 } 14 15 // Structured version of Claims Section, as referenced at 16 // https://tools.ietf.org/html/rfc7519#section-4.1 17 // See examples for how to use this with your own claim types 18 type StandardClaims struct { 19 Audience string `json:"aud,omitempty"` 20 ExpiresAt int64 `json:"exp,omitempty"` 21 Id string `json:"jti,omitempty"` 22 IssuedAt int64 `json:"iat,omitempty"` 23 Issuer string `json:"iss,omitempty"` 24 NotBefore int64 `json:"nbf,omitempty"` 25 Subject string `json:"sub,omitempty"` 26 } 27 28 // Validates time based claims "exp, iat, nbf". 29 // There is no accounting for clock skew. 30 // As well, if any of the above claims are not in the token, it will still 31 // be considered a valid claim. 32 func (c StandardClaims) Valid() error { 33 vErr := new(ValidationError) 34 now := TimeFunc().Unix() 35 36 // The claims below are optional, by default, so if they are set to the 37 // default value in Go, let's not fail the verification for them. 38 if !c.VerifyExpiresAt(now, false) { 39 delta := time.Unix(now, 0).Sub(time.Unix(c.ExpiresAt, 0)) 40 vErr.Inner = fmt.Errorf("token is expired by %v", delta) 41 vErr.Errors |= ValidationErrorExpired 42 } 43 44 if !c.VerifyIssuedAt(now, false) { 45 vErr.Inner = fmt.Errorf("Token used before issued") 46 vErr.Errors |= ValidationErrorIssuedAt 47 } 48 49 if !c.VerifyNotBefore(now, false) { 50 vErr.Inner = fmt.Errorf("token is not valid yet") 51 vErr.Errors |= ValidationErrorNotValidYet 52 } 53 54 if vErr.valid() { 55 return nil 56 } 57 58 return vErr 59 } 60 61 // Compares the aud claim against cmp. 62 // If required is false, this method will return true if the value matches or is unset 63 func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool { 64 return verifyAud([]string{c.Audience}, cmp, req) 65 } 66 67 // Compares the exp claim against cmp. 68 // If required is false, this method will return true if the value matches or is unset 69 func (c *StandardClaims) VerifyExpiresAt(cmp int64, req bool) bool { 70 return verifyExp(c.ExpiresAt, cmp, req) 71 } 72 73 // Compares the iat claim against cmp. 74 // If required is false, this method will return true if the value matches or is unset 75 func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool) bool { 76 return verifyIat(c.IssuedAt, cmp, req) 77 } 78 79 // Compares the iss claim against cmp. 80 // If required is false, this method will return true if the value matches or is unset 81 func (c *StandardClaims) VerifyIssuer(cmp string, req bool) bool { 82 return verifyIss(c.Issuer, cmp, req) 83 } 84 85 // Compares the nbf claim against cmp. 86 // If required is false, this method will return true if the value matches or is unset 87 func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool) bool { 88 return verifyNbf(c.NotBefore, cmp, req) 89 } 90 91 // ----- helpers 92 93 func verifyAud(aud []string, cmp string, required bool) bool { 94 if len(aud) == 0 { 95 return !required 96 } 97 // use a var here to keep constant time compare when looping over a number of claims 98 result := false 99 100 var stringClaims string 101 for _, a := range aud { 102 if subtle.ConstantTimeCompare([]byte(a), []byte(cmp)) != 0 { 103 result = true 104 } 105 stringClaims = stringClaims + a 106 } 107 108 // case where "" is sent in one or many aud claims 109 if len(stringClaims) == 0 { 110 return !required 111 } 112 113 return result 114 } 115 116 func verifyExp(exp int64, now int64, required bool) bool { 117 if exp == 0 { 118 return !required 119 } 120 return now <= exp 121 } 122 123 func verifyIat(iat int64, now int64, required bool) bool { 124 if iat == 0 { 125 return !required 126 } 127 return now >= iat 128 } 129 130 func verifyIss(iss string, cmp string, required bool) bool { 131 if iss == "" { 132 return !required 133 } 134 if subtle.ConstantTimeCompare([]byte(iss), []byte(cmp)) != 0 { 135 return true 136 } else { 137 return false 138 } 139 } 140 141 func verifyNbf(nbf int64, now int64, required bool) bool { 142 if nbf == 0 { 143 return !required 144 } 145 return now >= nbf 146 }