package jwt import ( "fmt" "time" ) // Claims must just have a Valid method that determines // if the token is invalid for any supported reason type Claims interface { Valid() error } // RegisteredClaims are a structured version of the JWT Claims Set, // restricted to Registered Claim Names, as referenced at // https://datatracker.ietf.org/doc/html/rfc7519#section-4.1 // // This type can be used on its own, but then additional private and // public claims embedded in the JWT will not be parsed. The typical usecase // therefore is to embedded this in a user-defined claim type. // // See examples for how to use this with your own claim types. type RegisteredClaims struct { // the `iss` (Issuer) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.1 Issuer string `json:"iss,omitempty"` // the `sub` (Subject) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.2 Subject string `json:"sub,omitempty"` // the `aud` (Audience) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3 Audience ClaimStrings `json:"aud,omitempty"` // the `exp` (Expiration Time) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4 ExpiresAt *NumericDate `json:"exp,omitempty"` // the `nbf` (Not Before) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.5 NotBefore *NumericDate `json:"nbf,omitempty"` // the `iat` (Issued At) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6 IssuedAt *NumericDate `json:"iat,omitempty"` // the `jti` (JWT ID) claim. See https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.7 ID string `json:"jti,omitempty"` } func (c RegisteredClaims) GetExpiryAt() *NumericDate { return c.ExpiresAt } func (c RegisteredClaims) GetNotBefore() *NumericDate { return c.NotBefore } func (c RegisteredClaims) GetIssuedAt() *NumericDate { return c.IssuedAt } func (c RegisteredClaims) GetAudience() ClaimStrings { return c.Audience } func (c RegisteredClaims) GetIssuer() string { return c.Issuer } // Valid validates time based claims "exp, iat, nbf". // There is no accounting for clock skew. // As well, if any of the above claims are not in the token, it will still // be considered a valid claim. // // Deprecated: This function should not be called directly, rather a claim should be validated using // the Validator struct. func (c RegisteredClaims) Valid() error { return NewValidator().Validate(c) } // VerifyAudience compares the aud claim against cmp. // If required is false, this method will return true if the value matches or is unset func (c *RegisteredClaims) VerifyAudience(cmp string, req bool) bool { return NewValidator().VerifyAudience(c, cmp, req) } // VerifyExpiresAt compares the exp claim against cmp (cmp < exp). // If req is false, it will return true, if exp is unset. func (c *RegisteredClaims) VerifyExpiresAt(cmp time.Time, req bool) bool { return NewValidator().VerifyExpiresAt(c, cmp, req) } // VerifyIssuedAt compares the iat claim against cmp (cmp >= iat). // If req is false, it will return true, if iat is unset. func (c *RegisteredClaims) VerifyIssuedAt(cmp time.Time, req bool) bool { return NewValidator().VerifyIssuedAt(c, cmp, req) } // VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf). // If req is false, it will return true, if nbf is unset. func (c *RegisteredClaims) VerifyNotBefore(cmp time.Time, req bool) bool { return NewValidator().VerifyNotBefore(c, cmp, req) } // VerifyIssuer compares the iss claim against cmp. // If required is false, this method will return true if the value matches or is unset func (c *RegisteredClaims) VerifyIssuer(cmp string, req bool) bool { return NewValidator().VerifyIssuer(c, cmp, req) } // StandardClaims are a structured version of the JWT Claims Set, as referenced at // https://datatracker.ietf.org/doc/html/rfc7519#section-4. They do not follow the // specification exactly, since they were based on an earlier draft of the // specification and not updated. The main difference is that they only // support integer-based date fields and singular audiences. This might lead to // incompatibilities with other JWT implementations. The use of this is discouraged, instead // the newer RegisteredClaims struct should be used. // // Deprecated: Use RegisteredClaims instead for a forward-compatible way to access registered claims in a struct. 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"` } // Valid validates time based claims "exp, iat, nbf". There is no accounting for clock skew. // As well, if any of the above claims are not in the token, it will still // be considered a valid claim. func (c StandardClaims) Valid() error { vErr := new(ValidationError) now := TimeFunc().Unix() // 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) { delta := time.Unix(now, 0).Sub(time.Unix(c.ExpiresAt, 0)) vErr.Inner = fmt.Errorf("%s by %s", ErrTokenExpired, delta) vErr.Errors |= ValidationErrorExpired } if !c.VerifyIssuedAt(now, false) { vErr.Inner = ErrTokenUsedBeforeIssued vErr.Errors |= ValidationErrorIssuedAt } if !c.VerifyNotBefore(now, false) { vErr.Inner = ErrTokenNotValidYet vErr.Errors |= ValidationErrorNotValidYet } if vErr.valid() { return nil } return vErr } // VerifyAudience compares the aud claim against cmp. // If required is false, this method will return true if the value matches or is unset func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool { return verifyAud([]string{c.Audience}, cmp, req) } // VerifyExpiresAt compares the exp claim against cmp (cmp < exp). // If req is false, it will return true, if exp is unset. func (c *StandardClaims) VerifyExpiresAt(cmp int64, req bool) bool { if c.ExpiresAt == 0 { return verifyExp(nil, time.Unix(cmp, 0), req, 0) } t := time.Unix(c.ExpiresAt, 0) return verifyExp(&t, time.Unix(cmp, 0), req, 0) } // VerifyIssuedAt compares the iat claim against cmp (cmp >= iat). // If req is false, it will return true, if iat is unset. func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool) bool { if c.IssuedAt == 0 { return verifyIat(nil, time.Unix(cmp, 0), req, 0) } t := time.Unix(c.IssuedAt, 0) return verifyIat(&t, time.Unix(cmp, 0), req, 0) } // VerifyNotBefore compares the nbf claim against cmp (cmp >= nbf). // If req is false, it will return true, if nbf is unset. func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool) bool { if c.NotBefore == 0 { return verifyNbf(nil, time.Unix(cmp, 0), req, 0) } t := time.Unix(c.NotBefore, 0) return verifyNbf(&t, time.Unix(cmp, 0), req, 0) } // VerifyIssuer compares the iss claim against cmp. // If required is false, this method will return true if the value matches or is unset func (c *StandardClaims) VerifyIssuer(cmp string, req bool) bool { return verifyIss(c.Issuer, cmp, req) }