generated from datumforge/go-template
-
Notifications
You must be signed in to change notification settings - Fork 5
/
validator.go
107 lines (82 loc) · 2.76 KB
/
validator.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
package tokens
import (
"crypto/subtle"
jwt "github.com/golang-jwt/jwt/v5"
)
// Validator are able to verify that access and refresh tokens were issued by
// Datum and that their claims are valid (e.g. not expired).
type Validator interface {
// Verify an access or a refresh token after parsing and return its claims
Verify(tks string) (claims *Claims, err error)
// Parse an access or refresh token without verifying claims (e.g. to check an expired token)
Parse(tks string) (claims *Claims, err error)
}
// validator implements the Validator interface, allowing structs in this package to
// embed the validation code base and supply their own keyFunc; unifying functionality
type validator struct {
audience string
issuer string
keyFunc jwt.Keyfunc
}
// Verify an access or a refresh token after parsing and return its claims.
func (v *validator) Verify(tks string) (claims *Claims, err error) {
var token *jwt.Token
if token, err = jwt.ParseWithClaims(tks, &Claims{}, v.keyFunc); err != nil {
return nil, err
}
var ok bool
if claims, ok = token.Claims.(*Claims); ok && token.Valid {
if !claims.VerifyAudience(v.audience, true) {
return nil, ErrTokenInvalidAudience
}
if !claims.VerifyIssuer(v.issuer, true) {
return nil, ErrTokenInvalidIssuer
}
return claims, nil
}
return nil, ErrTokenInvalidClaims
}
// Parse an access or refresh token verifying its signature but without verifying its
// claims. This ensures that valid JWT tokens are still accepted but claims can be
// handled on a case-by-case basis; for example by validating an expired access token
// during reauthentication
func (v *validator) Parse(tks string) (claims *Claims, err error) {
method := GetAlgorithms()
parser := jwt.NewParser(jwt.WithValidMethods(method), jwt.WithoutClaimsValidation())
claims = &Claims{}
if _, err = parser.ParseWithClaims(tks, claims, v.keyFunc); err != nil {
return nil, err
}
return claims, nil
}
func (c *Claims) VerifyAudience(cmp string, req bool) bool {
return verifyAud(c.Audience, cmp, req)
}
func (c *Claims) VerifyIssuer(cmp string, req bool) bool {
return verifyIss(c.Issuer, cmp, req)
}
func verifyIss(iss string, cmp string, required bool) bool {
if iss == "" {
return !required
}
return subtle.ConstantTimeCompare([]byte(iss), []byte(cmp)) != 0
}
func verifyAud(aud []string, cmp string, required bool) bool {
if len(aud) == 0 {
return !required
}
// use a var here to keep constant time compare when looping over a number of claims
result := false
var stringClaims string
for _, a := range aud {
if subtle.ConstantTimeCompare([]byte(a), []byte(cmp)) != 0 {
result = true
}
stringClaims += a
}
// case where "" is sent in one or many aud claims
if len(stringClaims) == 0 {
return !required
}
return result
}