1package jwt
2
3import (
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
11type 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
18type 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.
32func (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) == 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) == false {
45		vErr.Inner = fmt.Errorf("Token used before issued")
46		vErr.Errors |= ValidationErrorIssuedAt
47	}
48
49	if c.VerifyNotBefore(now, false) == 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
63func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool {
64	return verifyAud(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
69func (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
75func (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
81func (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
87func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool) bool {
88	return verifyNbf(c.NotBefore, cmp, req)
89}
90
91// ----- helpers
92
93func verifyAud(aud []string, cmp string, required bool) bool {
94	if len(aud) == 0 {
95		return !required
96	}
97
98	for _, a := range aud {
99		if subtle.ConstantTimeCompare([]byte(a), []byte(cmp)) != 0 {
100			return true
101		}
102	}
103	return false
104}
105
106func verifyExp(exp int64, now int64, required bool) bool {
107	if exp == 0 {
108		return !required
109	}
110	return now <= exp
111}
112
113func verifyIat(iat int64, now int64, required bool) bool {
114	if iat == 0 {
115		return !required
116	}
117	return now >= iat
118}
119
120func verifyIss(iss string, cmp string, required bool) bool {
121	if iss == "" {
122		return !required
123	}
124	if subtle.ConstantTimeCompare([]byte(iss), []byte(cmp)) != 0 {
125		return true
126	} else {
127		return false
128	}
129}
130
131func verifyNbf(nbf int64, now int64, required bool) bool {
132	if nbf == 0 {
133		return !required
134	}
135	return now >= nbf
136}
137