...

Source file src/github.com/golang-jwt/jwt/v4/claims.go

Documentation: github.com/golang-jwt/jwt/v4

     1  package jwt
     2  
     3  import (
     4  	"crypto/subtle"
     5  	"fmt"
     6  	"time"
     7  )
     8  
     9  // Claims 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  // RegisteredClaims are a structured version of the JWT Claims Set,
    16  // restricted to Registered Claim Names, as referenced at
    17  // https://datatracker.ietf.org/doc/html/rfc7519#section-4.1
    18  //
    19  // This type can be used on its own, but then additional private and
    20  // public claims embedded in the JWT will not be parsed. The typical usecase
    21  // therefore is to embedded this in a user-defined claim type.
    22  //
    23  // See examples for how to use this with your own claim types.
    24  type RegisteredClaims struct {
    25  	// the `iss` (Issuer) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.1
    26  	Issuer string `json:"iss,omitempty"`
    27  
    28  	// the `sub` (Subject) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.2
    29  	Subject string `json:"sub,omitempty"`
    30  
    31  	// the `aud` (Audience) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3
    32  	Audience ClaimStrings `json:"aud,omitempty"`
    33  
    34  	// the `exp` (Expiration Time) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4
    35  	ExpiresAt *NumericDate `json:"exp,omitempty"`
    36  
    37  	// the `nbf` (Not Before) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.5
    38  	NotBefore *NumericDate `json:"nbf,omitempty"`
    39  
    40  	// the `iat` (Issued At) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6
    41  	IssuedAt *NumericDate `json:"iat,omitempty"`
    42  
    43  	// the `jti` (JWT ID) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.7
    44  	ID string `json:"jti,omitempty"`
    45  }
    46  
    47  // Valid validates time based claims "exp, iat, nbf".
    48  // There is no accounting for clock skew.
    49  // As well, if any of the above claims are not in the token, it will still
    50  // be considered a valid claim.
    51  func (c RegisteredClaims) Valid() error {
    52  	vErr := new(ValidationError)
    53  	now := TimeFunc()
    54  
    55  	// The claims below are optional, by default, so if they are set to the
    56  	// default value in Go, let's not fail the verification for them.
    57  	if !c.VerifyExpiresAt(now, false) {
    58  		delta := now.Sub(c.ExpiresAt.Time)
    59  		vErr.Inner = fmt.Errorf("%s by %s", ErrTokenExpired, delta)
    60  		vErr.Errors |= ValidationErrorExpired
    61  	}
    62  
    63  	if !c.VerifyIssuedAt(now, false) {
    64  		vErr.Inner = ErrTokenUsedBeforeIssued
    65  		vErr.Errors |= ValidationErrorIssuedAt
    66  	}
    67  
    68  	if !c.VerifyNotBefore(now, false) {
    69  		vErr.Inner = ErrTokenNotValidYet
    70  		vErr.Errors |= ValidationErrorNotValidYet
    71  	}
    72  
    73  	if vErr.valid() {
    74  		return nil
    75  	}
    76  
    77  	return vErr
    78  }
    79  
    80  // VerifyAudience compares the aud claim against cmp.
    81  // If required is false, this method will return true if the value matches or is unset
    82  func (c *RegisteredClaims) VerifyAudience(cmp string, req bool) bool {
    83  	return verifyAud(c.Audience, cmp, req)
    84  }
    85  
    86  // VerifyExpiresAt compares the exp claim against cmp (cmp < exp).
    87  // If req is false, it will return true, if exp is unset.
    88  func (c *RegisteredClaims) VerifyExpiresAt(cmp time.Time, req bool) bool {
    89  	if c.ExpiresAt == nil {
    90  		return verifyExp(nil, cmp, req)
    91  	}
    92  
    93  	return verifyExp(&c.ExpiresAt.Time, cmp, req)
    94  }
    95  
    96  // VerifyIssuedAt compares the iat claim against cmp (cmp >= iat).
    97  // If req is false, it will return true, if iat is unset.
    98  func (c *RegisteredClaims) VerifyIssuedAt(cmp time.Time, req bool) bool {
    99  	if c.IssuedAt == nil {
   100  		return verifyIat(nil, cmp, req)
   101  	}
   102  
   103  	return verifyIat(&c.IssuedAt.Time, cmp, req)
   104  }
   105  
   106  // VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf).
   107  // If req is false, it will return true, if nbf is unset.
   108  func (c *RegisteredClaims) VerifyNotBefore(cmp time.Time, req bool) bool {
   109  	if c.NotBefore == nil {
   110  		return verifyNbf(nil, cmp, req)
   111  	}
   112  
   113  	return verifyNbf(&c.NotBefore.Time, cmp, req)
   114  }
   115  
   116  // VerifyIssuer compares the iss claim against cmp.
   117  // If required is false, this method will return true if the value matches or is unset
   118  func (c *RegisteredClaims) VerifyIssuer(cmp string, req bool) bool {
   119  	return verifyIss(c.Issuer, cmp, req)
   120  }
   121  
   122  // StandardClaims are a structured version of the JWT Claims Set, as referenced at
   123  // https://datatracker.ietf.org/doc/html/rfc7519#section-4. They do not follow the
   124  // specification exactly, since they were based on an earlier draft of the
   125  // specification and not updated. The main difference is that they only
   126  // support integer-based date fields and singular audiences. This might lead to
   127  // incompatibilities with other JWT implementations. The use of this is discouraged, instead
   128  // the newer RegisteredClaims struct should be used.
   129  //
   130  // Deprecated: Use RegisteredClaims instead for a forward-compatible way to access registered claims in a struct.
   131  type StandardClaims struct {
   132  	Audience  string `json:"aud,omitempty"`
   133  	ExpiresAt int64  `json:"exp,omitempty"`
   134  	Id        string `json:"jti,omitempty"`
   135  	IssuedAt  int64  `json:"iat,omitempty"`
   136  	Issuer    string `json:"iss,omitempty"`
   137  	NotBefore int64  `json:"nbf,omitempty"`
   138  	Subject   string `json:"sub,omitempty"`
   139  }
   140  
   141  // Valid validates time based claims "exp, iat, nbf". There is no accounting for clock skew.
   142  // As well, if any of the above claims are not in the token, it will still
   143  // be considered a valid claim.
   144  func (c StandardClaims) Valid() error {
   145  	vErr := new(ValidationError)
   146  	now := TimeFunc().Unix()
   147  
   148  	// The claims below are optional, by default, so if they are set to the
   149  	// default value in Go, let's not fail the verification for them.
   150  	if !c.VerifyExpiresAt(now, false) {
   151  		delta := time.Unix(now, 0).Sub(time.Unix(c.ExpiresAt, 0))
   152  		vErr.Inner = fmt.Errorf("%s by %s", ErrTokenExpired, delta)
   153  		vErr.Errors |= ValidationErrorExpired
   154  	}
   155  
   156  	if !c.VerifyIssuedAt(now, false) {
   157  		vErr.Inner = ErrTokenUsedBeforeIssued
   158  		vErr.Errors |= ValidationErrorIssuedAt
   159  	}
   160  
   161  	if !c.VerifyNotBefore(now, false) {
   162  		vErr.Inner = ErrTokenNotValidYet
   163  		vErr.Errors |= ValidationErrorNotValidYet
   164  	}
   165  
   166  	if vErr.valid() {
   167  		return nil
   168  	}
   169  
   170  	return vErr
   171  }
   172  
   173  // VerifyAudience compares the aud claim against cmp.
   174  // If required is false, this method will return true if the value matches or is unset
   175  func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool {
   176  	return verifyAud([]string{c.Audience}, cmp, req)
   177  }
   178  
   179  // VerifyExpiresAt compares the exp claim against cmp (cmp < exp).
   180  // If req is false, it will return true, if exp is unset.
   181  func (c *StandardClaims) VerifyExpiresAt(cmp int64, req bool) bool {
   182  	if c.ExpiresAt == 0 {
   183  		return verifyExp(nil, time.Unix(cmp, 0), req)
   184  	}
   185  
   186  	t := time.Unix(c.ExpiresAt, 0)
   187  	return verifyExp(&t, time.Unix(cmp, 0), req)
   188  }
   189  
   190  // VerifyIssuedAt compares the iat claim against cmp (cmp >= iat).
   191  // If req is false, it will return true, if iat is unset.
   192  func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool) bool {
   193  	if c.IssuedAt == 0 {
   194  		return verifyIat(nil, time.Unix(cmp, 0), req)
   195  	}
   196  
   197  	t := time.Unix(c.IssuedAt, 0)
   198  	return verifyIat(&t, time.Unix(cmp, 0), req)
   199  }
   200  
   201  // VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf).
   202  // If req is false, it will return true, if nbf is unset.
   203  func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool) bool {
   204  	if c.NotBefore == 0 {
   205  		return verifyNbf(nil, time.Unix(cmp, 0), req)
   206  	}
   207  
   208  	t := time.Unix(c.NotBefore, 0)
   209  	return verifyNbf(&t, time.Unix(cmp, 0), req)
   210  }
   211  
   212  // VerifyIssuer compares the iss claim against cmp.
   213  // If required is false, this method will return true if the value matches or is unset
   214  func (c *StandardClaims) VerifyIssuer(cmp string, req bool) bool {
   215  	return verifyIss(c.Issuer, cmp, req)
   216  }
   217  
   218  // ----- helpers
   219  
   220  func verifyAud(aud []string, cmp string, required bool) bool {
   221  	if len(aud) == 0 {
   222  		return !required
   223  	}
   224  	// use a var here to keep constant time compare when looping over a number of claims
   225  	result := false
   226  
   227  	var stringClaims string
   228  	for _, a := range aud {
   229  		if subtle.ConstantTimeCompare([]byte(a), []byte(cmp)) != 0 {
   230  			result = true
   231  		}
   232  		stringClaims = stringClaims + a
   233  	}
   234  
   235  	// case where "" is sent in one or many aud claims
   236  	if len(stringClaims) == 0 {
   237  		return !required
   238  	}
   239  
   240  	return result
   241  }
   242  
   243  func verifyExp(exp *time.Time, now time.Time, required bool) bool {
   244  	if exp == nil {
   245  		return !required
   246  	}
   247  	return now.Before(*exp)
   248  }
   249  
   250  func verifyIat(iat *time.Time, now time.Time, required bool) bool {
   251  	if iat == nil {
   252  		return !required
   253  	}
   254  	return now.After(*iat) || now.Equal(*iat)
   255  }
   256  
   257  func verifyNbf(nbf *time.Time, now time.Time, required bool) bool {
   258  	if nbf == nil {
   259  		return !required
   260  	}
   261  	return now.After(*nbf) || now.Equal(*nbf)
   262  }
   263  
   264  func verifyIss(iss string, cmp string, required bool) bool {
   265  	if iss == "" {
   266  		return !required
   267  	}
   268  	return subtle.ConstantTimeCompare([]byte(iss), []byte(cmp)) != 0
   269  }
   270  

View as plain text