-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvalidator.go
More file actions
88 lines (78 loc) · 2.61 KB
/
Copy pathvalidator.go
File metadata and controls
88 lines (78 loc) · 2.61 KB
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
package nwt
import (
"errors"
"fmt"
"slices"
"time"
)
var (
// MinTime represents the minimum valid time for NWT claims, corresponding to the 0 Unix epoch.
MinTime = time.Unix(0, 0).UTC()
// MaxTime represents the maximum valid time for NWT claims, set to December 31, 9999.
MaxTime = time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC)
)
// Token validation errors
var (
ErrEmptyID = errors.New("token ID is empty")
ErrInvalidIssuedAt = errors.New("issued at claim is invalid")
ErrInvalidExpiration = errors.New("expiration claim is invalid")
ErrInvalidNotBefore = errors.New("not before claim is invalid")
ErrInvalidTimeWindow = errors.New("not before is after expiration")
ErrInvalidAudience = errors.New("audience claim is invalid")
ErrNotYetValid = errors.New("token not yet valid (before NotBefore)")
ErrExpired = errors.New("token expired (after Expiration)")
)
// Validator wraps the Validate method for validating Tokens.
// The token is considered valid iff Validate returns nil.
//
// Implementations may enforce different policies for what constitutes a valid token,
// but are generally expected to at least validate the time-based claims with [ValidateTimeClaims].
//
// As an example, check out [StrictValidator].
type Validator interface {
Validate(Token) error
}
// StrictValidator performs validation on the Token claims.
// It checks time-based claims with a configurable clock skew tolerance
// and verifies that the Audience claim contains an exact match of the specified identifier.
type StrictValidator struct {
Identifier string
ClockSkew time.Duration
}
func (v StrictValidator) Validate(t Token) error {
if t.ID == "" {
return ErrEmptyID
}
if err := ValidateTimeClaims(t, v.ClockSkew); err != nil {
return err
}
if len(t.Audience) > 0 {
if !slices.Contains(t.Audience, v.Identifier) {
return fmt.Errorf("%w: it doesn't contain an exact match of %q", ErrInvalidAudience, v.Identifier)
}
}
return nil
}
// ValidateTimeClaims checks that the Token's time-based claims are within valid bounds.
func ValidateTimeClaims(t Token, skew time.Duration) error {
if t.IssuedAt.Before(MinTime) || t.IssuedAt.After(MaxTime) {
return ErrInvalidIssuedAt
}
if t.Expiration.Before(MinTime) || t.Expiration.After(MaxTime) {
return ErrInvalidExpiration
}
if t.NotBefore.Before(MinTime) || t.NotBefore.After(MaxTime) {
return ErrInvalidNotBefore
}
if t.NotBefore.After(t.Expiration) {
return ErrInvalidTimeWindow
}
now := time.Now()
if now.Before(t.NotBefore.Add(-skew)) {
return ErrNotYetValid
}
if now.After(t.Expiration.Add(skew)) {
return ErrExpired
}
return nil
}