forked from lestrrat-go/jwx
/
verify.go
157 lines (138 loc) · 3.71 KB
/
verify.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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package jwt
import (
"errors"
"time"
"github.com/lestrrat-go/jwx/internal/option"
)
const (
optkeyAcceptableSkew = "acceptableSkew"
optkeyClock = "clock"
optkeyIssuer = "issuer"
optkeySubject = "subject"
optkeyAudience = "audience"
optkeyJwtid = "jwtid"
)
type Clock interface {
Now() time.Time
}
type ClockFunc func() time.Time
func (f ClockFunc) Now() time.Time {
return f()
}
// WithClock specifies the `Clock` to be used when verifying
// claims exp and nbf.
func WithClock(c Clock) Option {
return option.New(optkeyClock, c)
}
// WithAcceptableSkew specifies the duration in which exp and nbf
// claims may differ by. This value should be positive
func WithAcceptableSkew(dur time.Duration) Option {
return option.New(optkeyAcceptableSkew, dur)
}
// WithIssuer specifies that expected issuer value. If not specified,
// the value of issuer is not verified at all.
func WithIssuer(s string) Option {
return option.New(optkeyIssuer, s)
}
// WithSubject specifies that expected subject value. If not specified,
// the value of subject is not verified at all.
func WithSubject(s string) Option {
return option.New(optkeySubject, s)
}
// WithJwtID specifies that expected jti value. If not specified,
// the value of jti is not verified at all.
func WithJwtID(s string) Option {
return option.New(optkeyJwtid, s)
}
// WithAudience specifies that expected audience value.
// Verify will return true if one of the values in the `aud` element
// matches this value. If not specified, the value of issuer is not
// verified at all.
func WithAudience(s string) Option {
return option.New(optkeyAudience, s)
}
// Verify makes sure that the essential claims stand.
//
// See the various `WithXXX` functions for optional parameters
// that can control the behavior of this method.
func (t *Token) Verify(options ...Option) error {
var issuer string
var subject string
var audience string
var jwtid string
var clock Clock = ClockFunc(time.Now)
var skew time.Duration
for _, o := range options {
switch o.Name() {
case optkeyClock:
clock = o.Value().(Clock)
case optkeyAcceptableSkew:
skew = o.Value().(time.Duration)
case optkeyIssuer:
issuer = o.Value().(string)
case optkeySubject:
subject = o.Value().(string)
case optkeyAudience:
audience = o.Value().(string)
case optkeyJwtid:
jwtid = o.Value().(string)
}
}
// check for iss
if len(issuer) > 0 {
if v := t.GetIssuer(); v != "" && v != issuer {
return errors.New(`iss not satisfied`)
}
}
// check for jti
if len(jwtid) > 0 {
if v := t.GetJwtID(); v != "" && v != jwtid {
return errors.New(`jti not satisfied`)
}
}
// check for sub
if len(subject) > 0 {
if v := t.GetSubject(); v != "" && v != subject {
return errors.New(`sub not satisfied`)
}
}
// check for aud
if len(audience) > 0 {
var found bool
for _, v := range t.GetAudience() {
if v == audience {
found = true
break
}
}
if !found {
return errors.New(`aud not satisfied`)
}
}
// check for exp
if tv := t.GetExpiration(); !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.GetIssuedAt(); !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.GetNotBefore(); !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`)
}
}
return nil
}