Merge commit from fork

* Initial draft of adding text to ParseWithClaims

* Adjusted example and referring to the example in Parse functions

* Backporting logic from v5

* Added a test
This commit is contained in:
Christian Banse 2024-11-03 21:01:24 +01:00 committed by GitHub
parent 9358574a7a
commit 7b1c1c00a1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 36 additions and 22 deletions

View File

@ -93,7 +93,7 @@ func ExampleParseWithClaims_customClaimsType() {
// Output: bar test // Output: bar test
} }
// An example of parsing the error types using bitfield checks // An example of parsing the error types using [errors.Is].
func ExampleParse_errorChecking() { func ExampleParse_errorChecking() {
// Token from another example. This token is expired // Token from another example. This token is expired
var tokenString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJleHAiOjE1MDAwLCJpc3MiOiJ0ZXN0In0.HE7fK0xOQwFEr4WDgRWj4teRPZ6i3GLwD5YCm6Pwu_c" var tokenString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJleHAiOjE1MDAwLCJpc3MiOiJ0ZXN0In0.HE7fK0xOQwFEr4WDgRWj4teRPZ6i3GLwD5YCm6Pwu_c"
@ -106,6 +106,10 @@ func ExampleParse_errorChecking() {
fmt.Println("You look nice today") fmt.Println("You look nice today")
} else if errors.Is(err, jwt.ErrTokenMalformed) { } else if errors.Is(err, jwt.ErrTokenMalformed) {
fmt.Println("That's not even a token") fmt.Println("That's not even a token")
} else if errors.Is(err, jwt.ErrTokenUnverifiable) {
fmt.Println("We could not verify this token")
} else if errors.Is(err, jwt.ErrTokenSignatureInvalid) {
fmt.Println("This token has an invalid signature")
} else if errors.Is(err, jwt.ErrTokenExpired) || errors.Is(err, jwt.ErrTokenNotValidYet) { } else if errors.Is(err, jwt.ErrTokenExpired) || errors.Is(err, jwt.ErrTokenNotValidYet) {
// Token is either expired or not active yet // Token is either expired or not active yet
fmt.Println("Timing is everything") fmt.Println("Timing is everything")

View File

@ -36,19 +36,21 @@ func NewParser(options ...ParserOption) *Parser {
return p return p
} }
// Parse parses, validates, verifies the signature and returns the parsed token. // Parse parses, validates, verifies the signature and returns the parsed token. keyFunc will
// keyFunc will receive the parsed token and should return the key for validating. // receive the parsed token and should return the key for validating.
func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) { func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) {
return p.ParseWithClaims(tokenString, MapClaims{}, keyFunc) return p.ParseWithClaims(tokenString, MapClaims{}, keyFunc)
} }
// ParseWithClaims parses, validates, and verifies like Parse, but supplies a default object implementing the Claims // ParseWithClaims parses, validates, and verifies like Parse, but supplies a default object
// interface. This provides default values which can be overridden and allows a caller to use their own type, rather // implementing the Claims interface. This provides default values which can be overridden and
// than the default MapClaims implementation of Claims. // allows a caller to use their own type, rather than the default MapClaims implementation of
// Claims.
// //
// Note: If you provide a custom claim implementation that embeds one of the standard claims (such as RegisteredClaims), // Note: If you provide a custom claim implementation that embeds one of the standard claims (such
// make sure that a) you either embed a non-pointer version of the claims or b) if you are using a pointer, allocate the // as RegisteredClaims), make sure that a) you either embed a non-pointer version of the claims or
// proper memory for it before passing in the overall claims, otherwise you might run into a panic. // b) if you are using a pointer, allocate the proper memory for it before passing in the overall
// claims, otherwise you might run into a panic.
func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) { func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) {
token, parts, err := p.ParseUnverified(tokenString, claims) token, parts, err := p.ParseUnverified(tokenString, claims)
if err != nil { if err != nil {
@ -85,12 +87,17 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf
return token, &ValidationError{Inner: err, Errors: ValidationErrorUnverifiable} return token, &ValidationError{Inner: err, Errors: ValidationErrorUnverifiable}
} }
// Perform validation
token.Signature = parts[2]
if err := token.Method.Verify(strings.Join(parts[0:2], "."), token.Signature, key); err != nil {
return token, &ValidationError{Inner: err, Errors: ValidationErrorSignatureInvalid}
}
vErr := &ValidationError{} vErr := &ValidationError{}
// Validate Claims // Validate Claims
if !p.SkipClaimsValidation { if !p.SkipClaimsValidation {
if err := token.Claims.Valid(); err != nil { if err := token.Claims.Valid(); err != nil {
// If the Claims Valid returned an error, check if it is a validation error, // If the Claims Valid returned an error, check if it is a validation error,
// If it was another error type, create a ValidationError with a generic ClaimsInvalid flag set // If it was another error type, create a ValidationError with a generic ClaimsInvalid flag set
if e, ok := err.(*ValidationError); !ok { if e, ok := err.(*ValidationError); !ok {
@ -98,23 +105,15 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf
} else { } else {
vErr = e vErr = e
} }
}
}
// Perform validation
token.Signature = parts[2]
if err = token.Method.Verify(strings.Join(parts[0:2], "."), token.Signature, key); err != nil {
vErr.Inner = err
vErr.Errors |= ValidationErrorSignatureInvalid
}
if vErr.valid() {
token.Valid = true
return token, nil
}
return token, vErr return token, vErr
} }
}
// No errors so far, token is valid.
token.Valid = true
return token, nil
}
// ParseUnverified parses the token but doesn't validate the signature. // ParseUnverified parses the token but doesn't validate the signature.
// //

View File

@ -111,6 +111,17 @@ var jwtTestData = []struct {
nil, nil,
jwt.SigningMethodRS256, jwt.SigningMethodRS256,
}, },
{
"basic invalid and expired",
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIiLCJleHAiOjEyMzR9.IbFvatLIJ2Z7B_MAaeIaRZsRSQF1CDzmAE0osHII3WfRTbPavonrDXz-p2Ap_oh9LT2lyohL_jCLoVcpTyu7K3Rt-hdgxZ1_r1StwM1we0SqW2BFFeXCzyS9SLf2YTaVR35lVvfwwlCpPBgOw1SBbczm9m6yPgA9Afsvw_lG_GU2civvG0UzHXxbzWWvJoflGokJDuoHQiku2bfxReyNsoUGcLjx5tfkY7cPihM3CffPpRFYCVjv_abHYelZWpVjdGULQyJDInGYqO8oANqNTtjui7aqxBpcFCUBwVVgktM4Q6Dvj-o5LrdPyJSEl0b_R2JstFE5CbEZGN5anN1yHa",
defaultKeyFunc,
jwt.MapClaims{"foo": "bar", "exp": 1234.0},
false,
jwt.ValidationErrorSignatureInvalid,
[]error{jwt.ErrTokenSignatureInvalid, rsa.ErrVerification},
nil,
jwt.SigningMethodRS256,
},
{ {
"basic nokeyfunc", "basic nokeyfunc",
"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg", "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",