gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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 }