1 package jwt
2
3 import (
4 "bytes"
5 "crypto/subtle"
6 "encoding/json"
7 "errors"
8 "time"
9
10 "github.com/ory/x/errorsx"
11 jjson "gopkg.in/square/go-jose.v2/json"
12 )
13
14 var TimeFunc = time.Now
15
16
17
18
19
20
21 type MapClaims map[string]interface{}
22
23
24
25 func (m MapClaims) VerifyAudience(cmp string, req bool) bool {
26 var aud []string
27 switch v := m["aud"].(type) {
28 case []string:
29 aud = v
30 case []interface{}:
31 for _, a := range v {
32 vs, ok := a.(string)
33 if !ok {
34 return false
35 }
36 aud = append(aud, vs)
37 }
38 case string:
39 aud = append(aud, v)
40 default:
41 return false
42 }
43 return verifyAud(aud, cmp, req)
44 }
45
46
47
48 func (m MapClaims) VerifyExpiresAt(cmp int64, req bool) bool {
49 if v, ok := m.toInt64("exp"); ok {
50 return verifyExp(v, cmp, req)
51 }
52 return !req
53 }
54
55
56
57 func (m MapClaims) VerifyIssuedAt(cmp int64, req bool) bool {
58 if v, ok := m.toInt64("iat"); ok {
59 return verifyIat(v, cmp, req)
60 }
61 return !req
62 }
63
64
65
66 func (m MapClaims) VerifyIssuer(cmp string, req bool) bool {
67 iss, _ := m["iss"].(string)
68 return verifyIss(iss, cmp, req)
69 }
70
71
72
73 func (m MapClaims) VerifyNotBefore(cmp int64, req bool) bool {
74 if v, ok := m.toInt64("nbf"); ok {
75 return verifyNbf(v, cmp, req)
76 }
77
78 return !req
79 }
80
81 func (m MapClaims) toInt64(claim string) (int64, bool) {
82 switch t := m[claim].(type) {
83 case float64:
84 return int64(t), true
85 case int64:
86 return t, true
87 case json.Number:
88 v, err := t.Int64()
89 if err == nil {
90 return v, true
91 }
92 vf, err := t.Float64()
93 if err != nil {
94 return 0, false
95 }
96
97 return int64(vf), true
98 }
99 return 0, false
100 }
101
102
103
104
105
106 func (m MapClaims) Valid() error {
107 vErr := new(ValidationError)
108 now := TimeFunc().Unix()
109
110 if !m.VerifyExpiresAt(now, false) {
111 vErr.Inner = errors.New("Token is expired")
112 vErr.Errors |= ValidationErrorExpired
113 }
114
115 if !m.VerifyIssuedAt(now, false) {
116 vErr.Inner = errors.New("Token used before issued")
117 vErr.Errors |= ValidationErrorIssuedAt
118 }
119
120 if !m.VerifyNotBefore(now, false) {
121 vErr.Inner = errors.New("Token is not valid yet")
122 vErr.Errors |= ValidationErrorNotValidYet
123 }
124
125 if vErr.valid() {
126 return nil
127 }
128
129 return vErr
130 }
131
132 func (m MapClaims) UnmarshalJSON(b []byte) error {
133
134
135
136
137
138 d := jjson.NewDecoder(bytes.NewReader(b))
139 mp := map[string]interface{}(m)
140 d.SetNumberType(jjson.UnmarshalIntOrFloat)
141 if err := d.Decode(&mp); err != nil {
142 return errorsx.WithStack(err)
143 }
144
145 return nil
146 }
147
148 func verifyAud(aud []string, cmp string, required bool) bool {
149 if len(aud) == 0 {
150 return !required
151 }
152
153 for _, a := range aud {
154 if subtle.ConstantTimeCompare([]byte(a), []byte(cmp)) != 0 {
155 return true
156 }
157 }
158 return false
159 }
160
161 func verifyExp(exp int64, now int64, required bool) bool {
162 if exp == 0 {
163 return !required
164 }
165 return now <= exp
166 }
167
168 func verifyIat(iat int64, now int64, required bool) bool {
169 if iat == 0 {
170 return !required
171 }
172 return now >= iat
173 }
174
175 func verifyIss(iss string, cmp string, required bool) bool {
176 if iss == "" {
177 return !required
178 }
179 if subtle.ConstantTimeCompare([]byte(iss), []byte(cmp)) != 0 {
180 return true
181 } else {
182 return false
183 }
184 }
185
186 func verifyNbf(nbf int64, now int64, required bool) bool {
187 if nbf == 0 {
188 return !required
189 }
190 return now >= nbf
191 }
192
View as plain text