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
}
// 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")

View File

@ -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
}
}
}
// 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
}
}
// No errors so far, token is valid.
token.Valid = true
return token, nil
}
// ParseUnverified parses the token but doesn't validate the signature.

View File

@ -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",