From 7b1c1c00a171c6c79bbdb40e4ce7d197060c1c2c Mon Sep 17 00:00:00 2001 From: Christian Banse Date: Sun, 3 Nov 2024 21:01:24 +0100 Subject: [PATCH] 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 --- example_test.go | 6 +++++- parser.go | 41 ++++++++++++++++++++--------------------- parser_test.go | 11 +++++++++++ 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/example_test.go b/example_test.go index ddf49ccb..3e3bb382 100644 --- a/example_test.go +++ b/example_test.go @@ -93,7 +93,7 @@ func ExampleParseWithClaims_customClaimsType() { // 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() { // Token from another example. This token is expired var tokenString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJleHAiOjE1MDAwLCJpc3MiOiJ0ZXN0In0.HE7fK0xOQwFEr4WDgRWj4teRPZ6i3GLwD5YCm6Pwu_c" @@ -106,6 +106,10 @@ func ExampleParse_errorChecking() { fmt.Println("You look nice today") } else if errors.Is(err, jwt.ErrTokenMalformed) { 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) { // Token is either expired or not active yet fmt.Println("Timing is everything") diff --git a/parser.go b/parser.go index c0a6f692..9dd36e5a 100644 --- a/parser.go +++ b/parser.go @@ -36,19 +36,21 @@ func NewParser(options ...ParserOption) *Parser { return p } -// Parse parses, validates, verifies the signature and returns the parsed token. -// keyFunc will receive the parsed token and should return the key for validating. +// Parse parses, validates, verifies the signature and returns the parsed token. keyFunc will +// receive the parsed token and should return the key for validating. func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) { return p.ParseWithClaims(tokenString, MapClaims{}, keyFunc) } -// ParseWithClaims parses, validates, and verifies like Parse, but supplies a default object implementing the Claims -// interface. This provides default values which can be overridden and allows a caller to use their own type, rather -// than the default MapClaims implementation of Claims. +// ParseWithClaims parses, validates, and verifies like Parse, but supplies a default object +// implementing the Claims interface. This provides default values which can be overridden and +// 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), -// make sure that a) you either embed a non-pointer version of the claims or 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. +// Note: If you provide a custom claim implementation that embeds one of the standard claims (such +// as RegisteredClaims), make sure that a) you either embed a non-pointer version of the claims or +// 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) { token, parts, err := p.ParseUnverified(tokenString, claims) if err != nil { @@ -85,12 +87,17 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf 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{} // Validate Claims if !p.SkipClaimsValidation { if err := token.Claims.Valid(); err != nil { - // 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 e, ok := err.(*ValidationError); !ok { @@ -98,22 +105,14 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf } else { vErr = e } + return token, vErr } } - // 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 - } + // No errors so far, token is valid. + token.Valid = true - return token, vErr + return token, nil } // ParseUnverified parses the token but doesn't validate the signature. diff --git a/parser_test.go b/parser_test.go index 26a168e9..54cbfec5 100644 --- a/parser_test.go +++ b/parser_test.go @@ -111,6 +111,17 @@ var jwtTestData = []struct { nil, 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", "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg",