-
Notifications
You must be signed in to change notification settings - Fork 19
/
verify.go
172 lines (151 loc) · 6.68 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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
package jwt
import (
"encoding/json"
"errors"
)
// Verify decodes, verifies and validates the standard JWT claims
// of the given "token" using the algorithm and
// the secret key that this token was generated with.
//
// It returns a VerifiedToken which can be used to
// read the standard claims and some read-only information about the token.
// That VerifiedToken contains a `Claims` method, useful
// to bind the token's payload(claims) to a custom Go struct or a map when necessary.
//
// The last variadic input argument is optional, can be used
// for further claims validations before exit.
// Returns the verified token information.
//
// Example Code:
//
// verifiedToken, err := jwt.Verify(jwt.HS256, []byte("secret"), token)
// [handle error...]
// var claims map[string]interface{}
// verifiedToken.Claims(&claims)
func Verify(alg Alg, key PublicKey, token []byte, validators ...TokenValidator) (*VerifiedToken, error) {
return verifyToken(alg, key, nil, token, nil, validators...)
}
// VerifyEncrypted same as `Verify` but it decrypts the payload part with the given "decrypt" function.
// The "decrypt" function is called AFTER base64-decode and BEFORE Unmarshal.
// Look the `GCM` function for details.
func VerifyEncrypted(alg Alg, key PublicKey, decrypt InjectFunc, token []byte, validators ...TokenValidator) (*VerifiedToken, error) {
return verifyToken(alg, key, decrypt, token, nil, validators...)
}
// VerifyWithHeaderValidator same as `Verify` but it accepts a custom header validator too.
func VerifyWithHeaderValidator(alg Alg, key PublicKey, token []byte, headerValidator HeaderValidator, validators ...TokenValidator) (*VerifiedToken, error) {
return verifyToken(alg, key, nil, token, headerValidator, validators...)
}
// VerifyEncryptedWithHeaderValidator same as `VerifyEncrypted` but it accepts a custom header validator too.
func VerifyEncryptedWithHeaderValidator(alg Alg, key PublicKey, decrypt InjectFunc, token []byte, headerValidator HeaderValidator, validators ...TokenValidator) (*VerifiedToken, error) {
return verifyToken(alg, key, decrypt, token, headerValidator, validators...)
}
func verifyToken(alg Alg, key PublicKey, decrypt InjectFunc, token []byte, headerValidator HeaderValidator, validators ...TokenValidator) (*VerifiedToken, error) {
if len(token) == 0 {
return nil, ErrMissing
}
header, payload, signature, err := decodeToken(alg, key, token, headerValidator)
if err != nil {
return nil, err
}
if decrypt != nil {
payload, err = decrypt(payload)
if err != nil {
return nil, err
}
}
var standardClaims Claims
standardClaimsErr := json.Unmarshal(payload, &standardClaims) // Use the standard one instead of the custom, no need to support "required" feature here.
// Do not exist on this error now, the payload may not be a JSON one.
if standardClaimsErr != nil {
var secondChange claimsSecondChance // try again with a different structure, which always converted to the standard jwt claims.
if err = json.Unmarshal(payload, &secondChange); err != nil {
err = errPayloadNotJSON // allow validators to catch this error.
}
standardClaims = secondChange.toClaims()
} else {
err = validateClaims(Clock(), standardClaims)
}
for _, validator := range validators {
// A token validator can skip the builtin validation and return a nil error,
// in that case the previous error is skipped.
if err = validator.ValidateToken(token, standardClaims, err); err != nil {
break
}
}
if err != nil {
// Exit on parsing standard claims error(when Plain is missing) or standard claims validation error or custom validators.
return nil, err
}
verifiedTok := &VerifiedToken{
Token: token,
Header: header,
Payload: payload,
Signature: signature,
StandardClaims: standardClaims,
// We could store the standard claims error when Plain token validator is applied
// but there is no a single case of its usability, so we don't, unless is requested.
}
return verifiedTok, nil
}
// VerifiedToken holds the information about a verified token.
// Look `Verify` for more.
type VerifiedToken struct {
Token []byte // The original token.
Header []byte // The header (decoded) part.
Payload []byte // The payload (decoded) part.
Signature []byte // The signature (decoded) part.
StandardClaims Claims // Any standard claims extracted from the payload.
}
// Claims decodes the token's payload to the "dest".
// If the application requires custom claims, this is the method to Go.
//
// It calls the `Unmarshal(t.Payload, dest)` package-level function .
// When called, it decodes the token's payload (aka claims)
// to the "dest" pointer of a struct or map value.
// Note that the `StandardClaims` field is always set,
// as it contains the standard JWT claims,
// and validated at the `Verify` function itself,
// therefore NO FURTHER STEP is required
// to validate the "exp", "iat" and "nbf" claims.
func (t *VerifiedToken) Claims(dest interface{}) error {
return Unmarshal(t.Payload, dest)
}
var errPayloadNotJSON = errors.New("jwt: payload is not a type of JSON") // malformed JSON or it's not a JSON at all.
// Plain can be provided as a Token Validator at `Verify` and `VerifyEncrypted` functions
// to allow tokens with plain payload (no JSON or malformed JSON) to be successfully validated.
//
// Usage:
//
// verifiedToken, err := jwt.Verify(jwt.HS256, []byte("secret"), token, jwt.Plain)
// [handle error...]
// [verifiedToken.Payload...]
var Plain = TokenValidatorFunc(func(token []byte, standardClaims Claims, err error) error {
if err == errPayloadNotJSON {
return nil // skip this error entirely.
}
return err
})
type (
// TokenValidator provides further token and claims validation.
TokenValidator interface {
// ValidateToken accepts the token, the claims extracted from that
// and any error that may caused by claims validation (e.g. ErrExpired)
// or the previous validator.
// A token validator can skip the builtin validation and return a nil error.
// Usage:
// func(v *myValidator) ValidateToken(token []byte, standardClaims Claims, err error) error {
// if err!=nil { return err } <- to respect the previous error
// // otherwise return nil or any custom error.
// }
//
// Look `Blocklist`, `Expected` and `Leeway` for builtin implementations.
ValidateToken(token []byte, standardClaims Claims, err error) error
}
// TokenValidatorFunc is the interface-as-function shortcut for a TokenValidator.
TokenValidatorFunc func(token []byte, standardClaims Claims, err error) error
)
// ValidateToken completes the ValidateToken interface.
// It calls itself.
func (fn TokenValidatorFunc) ValidateToken(token []byte, standardClaims Claims, err error) error {
return fn(token, standardClaims, err)
}