Skip to content

Commit f9abfd6

Browse files
committed
Implementing Is(err) bool to support Go 1.13 style error checking
Fixes #135 by implementing `Is(err) bool` for the `ValidationError`. It both checks for the actual inner error message as well as our error flags for maximum backwards compatibility
1 parent a725c1f commit f9abfd6

File tree

3 files changed

+51
-13
lines changed

3 files changed

+51
-13
lines changed

errors.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ var (
99
ErrInvalidKey = errors.New("key is invalid")
1010
ErrInvalidKeyType = errors.New("key is of invalid type")
1111
ErrHashUnavailable = errors.New("the requested hash function is unavailable")
12+
13+
ErrTokenMalformed = errors.New("token is malformed")
14+
ErrTokenExpired = errors.New("token is expired")
15+
ErrTokenBeforeIssued = errors.New("token used before issued")
16+
ErrTokenNotValidYet = errors.New("token is not valid yet")
1217
)
1318

1419
// The errors that might occur when parsing and validating a token
@@ -62,3 +67,27 @@ func (e *ValidationError) Unwrap() error {
6267
func (e *ValidationError) valid() bool {
6368
return e.Errors == 0
6469
}
70+
71+
// Is checks if this ValidationError is of the supplied error type. We are first checking for the exact error message
72+
// by suppling the inner message. If that fails, we the error flags. This way we can supply custom error messages
73+
// and still use errors.Is using the pre-supplied error variables.
74+
func (e *ValidationError) Is(err error) bool {
75+
// Check, if our inner error is a direct match
76+
if errors.Is(errors.Unwrap(e), err) {
77+
return true
78+
}
79+
80+
// Otherwise, we need to match using our error flags
81+
switch err {
82+
case ErrTokenMalformed:
83+
return e.Errors&ValidationErrorExpired != 0
84+
case ErrTokenExpired:
85+
return e.Errors&ValidationErrorExpired != 0
86+
case ErrTokenNotValidYet:
87+
return e.Errors&ValidationErrorNotValidYet != 0
88+
case ErrTokenBeforeIssued:
89+
return e.Errors&ValidationErrorIssuedAt != 0
90+
}
91+
92+
return false
93+
}

example_test.go

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

33
import (
4+
"errors"
45
"fmt"
56
"time"
67

@@ -94,19 +95,25 @@ func ExampleParseWithClaims_customClaimsType() {
9495

9596
// An example of parsing the error types using bitfield checks
9697
func ExampleParse_errorChecking() {
98+
var (
99+
token *jwt.Token
100+
ve *jwt.ValidationError
101+
err error
102+
)
103+
97104
// Token from another example. This token is expired
98-
var tokenString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJleHAiOjE1MDAwLCJpc3MiOiJ0ZXN0In0.HE7fK0xOQwFEr4WDgRWj4teRPZ6i3GLwD5YCm6Pwu_c"
105+
tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJleHAiOjE1MDAwLCJpc3MiOiJ0ZXN0In0.HE7fK0xOQwFEr4WDgRWj4teRPZ6i3GLwD5YCm6Pwu_c"
99106

100-
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
107+
token, err = jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
101108
return []byte("AllYourBase"), nil
102109
})
103110

104111
if token.Valid {
105112
fmt.Println("You look nice today")
106-
} else if ve, ok := err.(*jwt.ValidationError); ok {
113+
} else if errors.As(err, &ve) {
107114
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
108115
fmt.Println("That's not even a token")
109-
} else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
116+
} else if errors.Is(err, jwt.ErrTokenExpired) || errors.Is(err, jwt.ErrTokenNotValidYet) {
110117
// Token is either expired or not active yet
111118
fmt.Println("Timing is everything")
112119
} else {

parser_test.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"crypto"
55
"crypto/rsa"
66
"encoding/json"
7+
"errors"
78
"fmt"
89
"reflect"
910
"testing"
@@ -325,6 +326,7 @@ func TestParser_Parse(t *testing.T) {
325326

326327
// Parse the token
327328
var token *jwt.Token
329+
var ve *jwt.ValidationError
328330
var err error
329331
var parser = data.parser
330332
if parser == nil {
@@ -361,15 +363,15 @@ func TestParser_Parse(t *testing.T) {
361363
if err == nil {
362364
t.Errorf("[%v] Expecting error. Didn't get one.", data.name)
363365
} else {
364-
365-
ve := err.(*jwt.ValidationError)
366-
// compare the bitfield part of the error
367-
if e := ve.Errors; e != data.errors {
368-
t.Errorf("[%v] Errors don't match expectation. %v != %v", data.name, e, data.errors)
369-
}
370-
371-
if err.Error() == errKeyFuncError.Error() && ve.Inner != errKeyFuncError {
372-
t.Errorf("[%v] Inner error does not match expectation. %v != %v", data.name, ve.Inner, errKeyFuncError)
366+
if errors.As(err, &ve) {
367+
// compare the bitfield part of the error
368+
if e := ve.Errors; e != data.errors {
369+
t.Errorf("[%v] Errors don't match expectation. %v != %v", data.name, e, data.errors)
370+
}
371+
372+
if err.Error() == errKeyFuncError.Error() && ve.Inner != errKeyFuncError {
373+
t.Errorf("[%v] Inner error does not match expectation. %v != %v", data.name, ve.Inner, errKeyFuncError)
374+
}
373375
}
374376
}
375377
}

0 commit comments

Comments
 (0)