Skip to content

Commit 1539832

Browse files
committed
More consistent way of handling composite errors
1 parent 66e2e01 commit 1539832

File tree

6 files changed

+48
-65
lines changed

6 files changed

+48
-65
lines changed

errors.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ var (
1010
ErrInvalidKeyType = errors.New("key is of invalid type")
1111
ErrHashUnavailable = errors.New("the requested hash function is unavailable")
1212

13-
ErrTokenMalformed = errors.New("token is malformed")
14-
ErrTokenUnverifiable = errors.New("token is unverifiable")
15-
ErrTokenSignatureInvalid = errors.New("token signature is invalid")
13+
ErrTokenMalformed = errors.New("token is malformed")
14+
ErrTokenUnverifiable = errors.New("token is unverifiable")
15+
ErrTokenRequiredClaimMissing = errors.New("a required claim is missing")
16+
ErrTokenSignatureInvalid = errors.New("token signature is invalid")
1617

1718
ErrTokenInvalidAudience = errors.New("token has invalid audience")
1819
ErrTokenExpired = errors.New("token is expired")
@@ -43,6 +44,7 @@ const (
4344
ValidationErrorClaimsInvalid // Generic claims validation error
4445
)
4546

47+
/*
4648
// NewValidationError is a helper for constructing a ValidationError with a string error message
4749
func NewValidationError(errorText string, errorFlags uint32) *ValidationError {
4850
return &ValidationError{
@@ -119,3 +121,4 @@ func (e *ValidationError) Is(err error) bool {
119121
120122
return false
121123
}
124+
*/

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module github.com/golang-jwt/jwt/v5
22

3-
go 1.16
3+
go 1.20

none.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package jwt
22

3+
import "fmt"
4+
35
// SigningMethodNone implements the none signing method. This is required by the spec
46
// but you probably should never use it.
57
var SigningMethodNone *signingMethodNone
@@ -13,7 +15,7 @@ type unsafeNoneMagicConstant string
1315

1416
func init() {
1517
SigningMethodNone = &signingMethodNone{}
16-
NoneSignatureTypeDisallowedError = NewValidationError("'none' signature type is not allowed", ValidationErrorSignatureInvalid)
18+
NoneSignatureTypeDisallowedError = fmt.Errorf("%w: 'none' signature type is not allowed", ErrTokenUnverifiable)
1719

1820
RegisterSigningMethod(SigningMethodNone.Alg(), func() SigningMethod {
1921
return SigningMethodNone
@@ -33,10 +35,7 @@ func (m *signingMethodNone) Verify(signingString, signature string, key interfac
3335
}
3436
// If signing method is none, signature must be an empty string
3537
if signature != "" {
36-
return NewValidationError(
37-
"'none' signing method with non-empty signature",
38-
ValidationErrorSignatureInvalid,
39-
)
38+
return fmt.Errorf("%w: 'none' signing method with non-empty signature", ErrTokenUnverifiable)
4039
}
4140

4241
// Accept 'none' signing method.

parser.go

Lines changed: 20 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -65,25 +65,25 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf
6565
}
6666
if !signingMethodValid {
6767
// signing method is not in the listed set
68-
return token, NewValidationError(fmt.Sprintf("signing method %v is invalid", alg), ValidationErrorSignatureInvalid)
68+
return token, fmt.Errorf("%w: signing method %v is invalid", ErrTokenSignatureInvalid, err)
6969
}
7070
}
7171

7272
// Lookup key
7373
var key interface{}
7474
if keyFunc == nil {
7575
// keyFunc was not provided. short circuiting validation
76-
return token, NewValidationError("no Keyfunc was provided.", ValidationErrorUnverifiable)
76+
return token, fmt.Errorf("%w: no keyfunc was provided", ErrTokenUnverifiable)
7777
}
7878
if key, err = keyFunc(token); err != nil {
79-
// keyFunc returned an error
80-
if ve, ok := err.(*ValidationError); ok {
81-
return token, ve
82-
}
83-
return token, &ValidationError{Inner: err, Errors: ValidationErrorUnverifiable}
79+
return token, fmt.Errorf("%w: error while executing keyfunc: %w", ErrTokenUnverifiable, err)
8480
}
8581

86-
vErr := &ValidationError{}
82+
// Perform signature validation
83+
token.Signature = parts[2]
84+
if err = token.Method.Verify(strings.Join(parts[0:2], "."), token.Signature, key); err != nil {
85+
return token, fmt.Errorf("%w: %w", ErrTokenSignatureInvalid, err)
86+
}
8787

8888
// Validate Claims
8989
if !p.skipClaimsValidation {
@@ -93,29 +93,14 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf
9393
}
9494

9595
if err := p.validator.Validate(claims); err != nil {
96-
// If the Claims Valid returned an error, check if it is a validation error,
97-
// If it was another error type, create a ValidationError with a generic ClaimsInvalid flag set
98-
if e, ok := err.(*ValidationError); !ok {
99-
vErr = &ValidationError{Inner: err, Errors: ValidationErrorClaimsInvalid}
100-
} else {
101-
vErr = e
102-
}
96+
return token, err
10397
}
10498
}
10599

106-
// Perform validation
107-
token.Signature = parts[2]
108-
if err = token.Method.Verify(strings.Join(parts[0:2], "."), token.Signature, key); err != nil {
109-
vErr.Inner = err
110-
vErr.Errors |= ValidationErrorSignatureInvalid
111-
}
112-
113-
if vErr.valid() {
114-
token.Valid = true
115-
return token, nil
116-
}
100+
// No errors so far, token is valid.
101+
token.Valid = true
117102

118-
return token, vErr
103+
return token, nil
119104
}
120105

121106
// ParseUnverified parses the token but doesn't validate the signature.
@@ -127,7 +112,7 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf
127112
func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Token, parts []string, err error) {
128113
parts = strings.Split(tokenString, ".")
129114
if len(parts) != 3 {
130-
return nil, parts, NewValidationError("token contains an invalid number of segments", ValidationErrorMalformed)
115+
return nil, parts, fmt.Errorf("%w: token contains an invalid number of segments", ErrTokenMalformed)
131116
}
132117

133118
token = &Token{Raw: tokenString}
@@ -136,20 +121,20 @@ func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Toke
136121
var headerBytes []byte
137122
if headerBytes, err = DecodeSegment(parts[0]); err != nil {
138123
if strings.HasPrefix(strings.ToLower(tokenString), "bearer ") {
139-
return token, parts, NewValidationError("tokenstring should not contain 'bearer '", ValidationErrorMalformed)
124+
return token, parts, fmt.Errorf("%w: tokenstring should not contain 'bearer '", ErrTokenMalformed)
140125
}
141-
return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
126+
return token, parts, fmt.Errorf("%w: %w", ErrTokenMalformed, err)
142127
}
143128
if err = json.Unmarshal(headerBytes, &token.Header); err != nil {
144-
return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
129+
return token, parts, fmt.Errorf("%w: %w", ErrTokenMalformed, err)
145130
}
146131

147132
// parse Claims
148133
var claimBytes []byte
149134
token.Claims = claims
150135

151136
if claimBytes, err = DecodeSegment(parts[1]); err != nil {
152-
return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
137+
return token, parts, fmt.Errorf("%w: %w", ErrTokenMalformed, err)
153138
}
154139
dec := json.NewDecoder(bytes.NewBuffer(claimBytes))
155140
if p.useJSONNumber {
@@ -163,16 +148,16 @@ func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Toke
163148
}
164149
// Handle decode error
165150
if err != nil {
166-
return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
151+
return token, parts, fmt.Errorf("%w: %w", ErrTokenMalformed, err)
167152
}
168153

169154
// Lookup signature method
170155
if method, ok := token.Header["alg"].(string); ok {
171156
if token.Method = GetSigningMethod(method); token.Method == nil {
172-
return token, parts, NewValidationError("signing method (alg) is unavailable.", ValidationErrorUnverifiable)
157+
return token, parts, fmt.Errorf("%w: signing method (alg) is unavailable", ErrTokenUnverifiable)
173158
}
174159
} else {
175-
return token, parts, NewValidationError("signing method (alg) is unspecified.", ValidationErrorUnverifiable)
160+
return token, parts, fmt.Errorf("%w: signing method (alg) is unspecified", ErrTokenUnverifiable)
176161
}
177162

178163
return token, parts, nil

parser_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ func TestParser_Parse(t *testing.T) {
359359

360360
// Parse the token
361361
var token *jwt.Token
362-
var ve *jwt.ValidationError
362+
//var ve *jwt.ValidationError
363363
var err error
364364
var parser = data.parser
365365
if parser == nil {
@@ -390,7 +390,7 @@ func TestParser_Parse(t *testing.T) {
390390
t.Errorf("[%v] Inconsistent behavior between returned error and token.Valid", data.name)
391391
}
392392

393-
if data.errors != 0 {
393+
/*if data.errors != 0 {
394394
if err == nil {
395395
t.Errorf("[%v] Expecting error. Didn't get one.", data.name)
396396
} else {
@@ -405,7 +405,7 @@ func TestParser_Parse(t *testing.T) {
405405
}
406406
}
407407
}
408-
}
408+
}*/
409409

410410
if data.err != nil {
411411
if err == nil {

validator.go

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package jwt
22

33
import (
44
"crypto/subtle"
5+
"errors"
56
"time"
67
)
78

@@ -56,8 +57,10 @@ func newValidator(opts ...ParserOption) *validator {
5657
// Validate validates the given claims. It will also perform any custom
5758
// validation if claims implements the CustomValidator interface.
5859
func (v *validator) Validate(claims Claims) error {
59-
var now time.Time
60-
vErr := new(ValidationError)
60+
var (
61+
now time.Time
62+
errs []error = make([]error, 0)
63+
)
6164

6265
// Check, if we have a time func
6366
if v.timeFunc != nil {
@@ -69,56 +72,49 @@ func (v *validator) Validate(claims Claims) error {
6972
// We always need to check the expiration time, but usage of the claim
7073
// itself is OPTIONAL
7174
if !v.VerifyExpiresAt(claims, now, false) {
72-
vErr.Inner = ErrTokenExpired
73-
vErr.Errors |= ValidationErrorExpired
75+
errs = append(errs, ErrTokenExpired)
7476
}
7577

7678
// We always need to check not-before, but usage of the claim itself is
7779
// OPTIONAL
7880
if !v.VerifyNotBefore(claims, now, false) {
79-
vErr.Inner = ErrTokenNotValidYet
80-
vErr.Errors |= ValidationErrorNotValidYet
81+
errs = append(errs, ErrTokenNotValidYet)
8182
}
8283

8384
// Check issued-at if the option is enabled
8485
if v.verifyIat && !v.VerifyIssuedAt(claims, now, false) {
85-
vErr.Inner = ErrTokenUsedBeforeIssued
86-
vErr.Errors |= ValidationErrorIssuedAt
86+
errs = append(errs, ErrTokenUsedBeforeIssued)
8787
}
8888

8989
// If we have an expected audience, we also require the audience claim
9090
if v.expectedAud != "" && !v.VerifyAudience(claims, v.expectedAud, true) {
91-
vErr.Inner = ErrTokenInvalidAudience
92-
vErr.Errors |= ValidationErrorAudience
91+
errs = append(errs, ErrTokenInvalidAudience)
9392
}
9493

9594
// If we have an expected issuer, we also require the issuer claim
9695
if v.expectedIss != "" && !v.VerifyIssuer(claims, v.expectedIss, true) {
97-
vErr.Inner = ErrTokenInvalidIssuer
98-
vErr.Errors |= ValidationErrorIssuer
96+
errs = append(errs, ErrTokenInvalidIssuer)
9997
}
10098

10199
// If we have an expected subject, we also require the subject claim
102100
if v.expectedSub != "" && !v.VerifySubject(claims, v.expectedSub, true) {
103-
vErr.Inner = ErrTokenInvalidSubject
104-
vErr.Errors |= ValidationErrorSubject
101+
errs = append(errs, ErrTokenInvalidSubject)
105102
}
106103

107104
// Finally, we want to give the claim itself some possibility to do some
108105
// additional custom validation based on a custom function
109106
cvt, ok := claims.(customClaims)
110107
if ok {
111108
if err := cvt.CustomValidation(); err != nil {
112-
vErr.Inner = err
113-
vErr.Errors |= ValidationErrorClaimsInvalid
109+
errs = append(errs, err)
114110
}
115111
}
116112

117-
if vErr.valid() {
113+
if len(errs) == 0 {
118114
return nil
119115
}
120116

121-
return vErr
117+
return errors.Join(errs...)
122118
}
123119

124120
// VerifyExpiresAt compares the exp claim in claims against cmp. This function

0 commit comments

Comments
 (0)