-
-
Notifications
You must be signed in to change notification settings - Fork 157
/
validate.go
120 lines (107 loc) · 2.51 KB
/
validate.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
package jwt
import (
"errors"
"fmt"
"time"
)
type Clock interface {
Now() time.Time
}
type ClockFunc func() time.Time
func (f ClockFunc) Now() time.Time {
return f()
}
// Validate makes sure that the essential claims stand.
//
// See the various `WithXXX` functions for optional parameters
// that can control the behavior of this method.
func Validate(t Token, options ...ValidateOption) error {
var issuer string
var subject string
var audience string
var jwtid string
var clock Clock = ClockFunc(time.Now)
var skew time.Duration
claimValues := make(map[string]interface{})
for _, o := range options {
switch o.Ident() {
case identClock{}:
clock = o.Value().(Clock)
case identAcceptableSkew{}:
skew = o.Value().(time.Duration)
case identIssuer{}:
issuer = o.Value().(string)
case identSubject{}:
subject = o.Value().(string)
case identAudience{}:
audience = o.Value().(string)
case identJwtid{}:
jwtid = o.Value().(string)
case identClaim{}:
claim := o.Value().(claimValue)
claimValues[claim.name] = claim.value
}
}
// check for iss
if len(issuer) > 0 {
if v := t.Issuer(); v != issuer {
return errors.New(`iss not satisfied`)
}
}
// check for jti
if len(jwtid) > 0 {
if v := t.JwtID(); v != jwtid {
return errors.New(`jti not satisfied`)
}
}
// check for sub
if len(subject) > 0 {
if v := t.Subject(); v != subject {
return errors.New(`sub not satisfied`)
}
}
// check for aud
if len(audience) > 0 {
var found bool
for _, v := range t.Audience() {
if v == audience {
found = true
break
}
}
if !found {
return errors.New(`aud not satisfied`)
}
}
// check for exp
if tv := t.Expiration(); !tv.IsZero() {
now := clock.Now().Truncate(time.Second)
ttv := tv.Truncate(time.Second)
if !now.Before(ttv.Add(skew)) {
return errors.New(`exp not satisfied`)
}
}
// check for iat
if tv := t.IssuedAt(); !tv.IsZero() {
now := clock.Now().Truncate(time.Second)
ttv := tv.Truncate(time.Second)
if now.Before(ttv.Add(-1 * skew)) {
return errors.New(`iat not satisfied`)
}
}
// check for nbf
if tv := t.NotBefore(); !tv.IsZero() {
now := clock.Now().Truncate(time.Second)
ttv := tv.Truncate(time.Second)
// now cannot be before t, so we check for now > t - skew
if !now.After(ttv.Add(-1 * skew)) {
return errors.New(`nbf not satisfied`)
}
}
for name, expectedValue := range claimValues {
if v, ok := t.Get(name); !ok || v != expectedValue {
return fmt.Errorf(`%v not satisfied`, name)
}
}
return nil
}