2015-10-29 21:45:16 +03:00
|
|
|
package jwt
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
2022-08-27 14:36:45 +03:00
|
|
|
// DefaultValidator is the default validator that is used, if no custom validator is supplied in a Parser.
|
|
|
|
var DefaultValidator = NewValidator()
|
|
|
|
|
2015-10-29 21:45:16 +03:00
|
|
|
type Parser struct {
|
2021-10-13 20:36:33 +03:00
|
|
|
// If populated, only these methods will be considered valid.
|
|
|
|
//
|
|
|
|
// Deprecated: In future releases, this field will not be exported anymore and should be set with an option to NewParser instead.
|
|
|
|
ValidMethods []string
|
|
|
|
|
|
|
|
// Use JSON Number format in JSON decoder.
|
|
|
|
//
|
|
|
|
// Deprecated: In future releases, this field will not be exported anymore and should be set with an option to NewParser instead.
|
|
|
|
UseJSONNumber bool
|
|
|
|
|
|
|
|
// Skip claims validation during token parsing.
|
|
|
|
//
|
|
|
|
// Deprecated: In future releases, this field will not be exported anymore and should be set with an option to NewParser instead.
|
|
|
|
SkipClaimsValidation bool
|
New Validation API
Some guidelines in designing the new validation API
* Previously, the `Valid` method was placed on the claim, which was always not entirely semantically correct, since the validity is concerning the token, not the claims. Although the validity of the token is based on the processing of the claims (such as `exp`). Therefore, the function `Valid` was removed from the `Claims` interface and the single canonical way to retrieve the validity of the token is to retrieve the `Valid` property of the `Token` struct.
* The previous fact was enhanced by the fact that most claims implementations had additional exported `VerifyXXX` functions, which are now removed
* All validation errors should be comparable with `errors.Is` to determine, why a particular validation has failed
* Developers want to adjust validation options. Popular options include:
* Leeway when processing exp, nbf, iat
* Not verifying `iat`, since this is actually just an informational claim. When purely looking at the standard, this should probably the default
* Verifying `aud` by default, which actually the standard sort of demands. We need to see how strong we want to enforce this
* Developers want to create their own claim types, mostly by embedding one of the existing types such as `RegisteredClaims`.
* Sometimes there is the need to further tweak the validation of a token by checking the value of a custom claim. Previously, this was possibly by overriding `Valid`. However, this was error-prone, e.g., if the original `Valid` was not called. Therefore, we should provide an easy way for *additional* checks, without by-passing the necessary validations
This leads to the following two major changes:
* The `Claims` interface now represents a set of functions that return the mandatory claims represented in a token, rather than just a `Valid` function. This is also more semantically correct.
* All validation tasks are offloaded to a new (optional) `Validator`, which can also be configured with appropriate options. If no custom validator was supplied, a default one is used.
2022-08-27 13:07:09 +03:00
|
|
|
|
|
|
|
validator *Validator
|
2021-10-13 20:36:33 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewParser creates a new Parser with the specified options
|
|
|
|
func NewParser(options ...ParserOption) *Parser {
|
2022-08-27 14:36:45 +03:00
|
|
|
p := &Parser{}
|
2021-10-13 20:36:33 +03:00
|
|
|
|
2022-08-27 14:36:45 +03:00
|
|
|
// Loop through our parsing options and apply them
|
2021-10-13 20:36:33 +03:00
|
|
|
for _, option := range options {
|
|
|
|
option(p)
|
|
|
|
}
|
|
|
|
|
|
|
|
return p
|
2015-10-29 21:45:16 +03:00
|
|
|
}
|
|
|
|
|
2021-10-15 15:48:31 +03:00
|
|
|
// Parse parses, validates, verifies the signature and returns the parsed token.
|
2015-10-29 21:45:16 +03:00
|
|
|
// keyFunc will receive the parsed token and should return the key for validating.
|
|
|
|
func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) {
|
2016-04-13 02:25:25 +03:00
|
|
|
return p.ParseWithClaims(tokenString, MapClaims{}, keyFunc)
|
2015-11-16 22:59:51 +03:00
|
|
|
}
|
|
|
|
|
2016-04-13 02:25:25 +03:00
|
|
|
func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) {
|
2016-07-21 19:37:13 +03:00
|
|
|
token, parts, err := p.ParseUnverified(tokenString, claims)
|
2016-04-13 00:32:24 +03:00
|
|
|
if err != nil {
|
2016-07-21 19:37:13 +03:00
|
|
|
return token, err
|
2015-10-29 21:45:16 +03:00
|
|
|
}
|
|
|
|
|
2015-11-03 02:22:08 +03:00
|
|
|
// Verify signing method is in the required set
|
|
|
|
if p.ValidMethods != nil {
|
|
|
|
var signingMethodValid = false
|
|
|
|
var alg = token.Method.Alg()
|
|
|
|
for _, m := range p.ValidMethods {
|
|
|
|
if m == alg {
|
|
|
|
signingMethodValid = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !signingMethodValid {
|
|
|
|
// signing method is not in the listed set
|
2016-04-13 03:31:30 +03:00
|
|
|
return token, NewValidationError(fmt.Sprintf("signing method %v is invalid", alg), ValidationErrorSignatureInvalid)
|
2015-11-03 02:22:08 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-29 21:45:16 +03:00
|
|
|
// Lookup key
|
|
|
|
var key interface{}
|
|
|
|
if keyFunc == nil {
|
|
|
|
// keyFunc was not provided. short circuiting validation
|
2016-04-13 03:31:30 +03:00
|
|
|
return token, NewValidationError("no Keyfunc was provided.", ValidationErrorUnverifiable)
|
2015-10-29 21:45:16 +03:00
|
|
|
}
|
|
|
|
if key, err = keyFunc(token); err != nil {
|
|
|
|
// keyFunc returned an error
|
Handle ValidationError returned by keyFunc in jwt.ParseWithClaims
Previously, returning a `jwt.ValidationError` from `jwt.Parse()` or
`jwt.ParseWithClaims()` would result values the error to be
ignored.
For example, when testing the signature while parsing the token, it
was not possible to return `jwt.ValidationErrorSignatureInvalid`.
The documentation shows an example for returning an `errors.Error`,
but this is not enough.
We change the `jwt.ParseWithClaims()`-function and check whether the
returned error from the `KeyFunc` is already a
`jwt.ValidationError`-type and return as-is.
This allows us to do the following:
token, err := jwt.ParseWithClaims(authToken, claims, func(token
*jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
vErr := new(jwt.ValidationError)
vErr.Errors = jwt.ValidationErrorSignatureInvalid
vErr.Inner = fmt.Errorf("invalid signature")
return nil, vErr
}
return []byte(MySecret), nil
})
The idea is to then be able to check the `Errors`-member:
} else if ve.Errors&jwt.ValidationErrorSignatureInvalid != 0 {
return fmt.Errorf("Authentication Token has invalid signature")
}
2017-06-28 10:16:23 +03:00
|
|
|
if ve, ok := err.(*ValidationError); ok {
|
|
|
|
return token, ve
|
|
|
|
}
|
2016-04-13 03:31:30 +03:00
|
|
|
return token, &ValidationError{Inner: err, Errors: ValidationErrorUnverifiable}
|
2015-10-29 21:45:16 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
vErr := &ValidationError{}
|
2015-11-16 22:59:51 +03:00
|
|
|
|
|
|
|
// Validate Claims
|
2016-06-21 23:11:54 +03:00
|
|
|
if !p.SkipClaimsValidation {
|
New Validation API
Some guidelines in designing the new validation API
* Previously, the `Valid` method was placed on the claim, which was always not entirely semantically correct, since the validity is concerning the token, not the claims. Although the validity of the token is based on the processing of the claims (such as `exp`). Therefore, the function `Valid` was removed from the `Claims` interface and the single canonical way to retrieve the validity of the token is to retrieve the `Valid` property of the `Token` struct.
* The previous fact was enhanced by the fact that most claims implementations had additional exported `VerifyXXX` functions, which are now removed
* All validation errors should be comparable with `errors.Is` to determine, why a particular validation has failed
* Developers want to adjust validation options. Popular options include:
* Leeway when processing exp, nbf, iat
* Not verifying `iat`, since this is actually just an informational claim. When purely looking at the standard, this should probably the default
* Verifying `aud` by default, which actually the standard sort of demands. We need to see how strong we want to enforce this
* Developers want to create their own claim types, mostly by embedding one of the existing types such as `RegisteredClaims`.
* Sometimes there is the need to further tweak the validation of a token by checking the value of a custom claim. Previously, this was possibly by overriding `Valid`. However, this was error-prone, e.g., if the original `Valid` was not called. Therefore, we should provide an easy way for *additional* checks, without by-passing the necessary validations
This leads to the following two major changes:
* The `Claims` interface now represents a set of functions that return the mandatory claims represented in a token, rather than just a `Valid` function. This is also more semantically correct.
* All validation tasks are offloaded to a new (optional) `Validator`, which can also be configured with appropriate options. If no custom validator was supplied, a default one is used.
2022-08-27 13:07:09 +03:00
|
|
|
// Make sure we have at least a default validator
|
|
|
|
if p.validator == nil {
|
2022-10-15 23:21:15 +03:00
|
|
|
p.validator = NewValidator()
|
New Validation API
Some guidelines in designing the new validation API
* Previously, the `Valid` method was placed on the claim, which was always not entirely semantically correct, since the validity is concerning the token, not the claims. Although the validity of the token is based on the processing of the claims (such as `exp`). Therefore, the function `Valid` was removed from the `Claims` interface and the single canonical way to retrieve the validity of the token is to retrieve the `Valid` property of the `Token` struct.
* The previous fact was enhanced by the fact that most claims implementations had additional exported `VerifyXXX` functions, which are now removed
* All validation errors should be comparable with `errors.Is` to determine, why a particular validation has failed
* Developers want to adjust validation options. Popular options include:
* Leeway when processing exp, nbf, iat
* Not verifying `iat`, since this is actually just an informational claim. When purely looking at the standard, this should probably the default
* Verifying `aud` by default, which actually the standard sort of demands. We need to see how strong we want to enforce this
* Developers want to create their own claim types, mostly by embedding one of the existing types such as `RegisteredClaims`.
* Sometimes there is the need to further tweak the validation of a token by checking the value of a custom claim. Previously, this was possibly by overriding `Valid`. However, this was error-prone, e.g., if the original `Valid` was not called. Therefore, we should provide an easy way for *additional* checks, without by-passing the necessary validations
This leads to the following two major changes:
* The `Claims` interface now represents a set of functions that return the mandatory claims represented in a token, rather than just a `Valid` function. This is also more semantically correct.
* All validation tasks are offloaded to a new (optional) `Validator`, which can also be configured with appropriate options. If no custom validator was supplied, a default one is used.
2022-08-27 13:07:09 +03:00
|
|
|
}
|
2022-03-26 17:13:03 +03:00
|
|
|
|
New Validation API
Some guidelines in designing the new validation API
* Previously, the `Valid` method was placed on the claim, which was always not entirely semantically correct, since the validity is concerning the token, not the claims. Although the validity of the token is based on the processing of the claims (such as `exp`). Therefore, the function `Valid` was removed from the `Claims` interface and the single canonical way to retrieve the validity of the token is to retrieve the `Valid` property of the `Token` struct.
* The previous fact was enhanced by the fact that most claims implementations had additional exported `VerifyXXX` functions, which are now removed
* All validation errors should be comparable with `errors.Is` to determine, why a particular validation has failed
* Developers want to adjust validation options. Popular options include:
* Leeway when processing exp, nbf, iat
* Not verifying `iat`, since this is actually just an informational claim. When purely looking at the standard, this should probably the default
* Verifying `aud` by default, which actually the standard sort of demands. We need to see how strong we want to enforce this
* Developers want to create their own claim types, mostly by embedding one of the existing types such as `RegisteredClaims`.
* Sometimes there is the need to further tweak the validation of a token by checking the value of a custom claim. Previously, this was possibly by overriding `Valid`. However, this was error-prone, e.g., if the original `Valid` was not called. Therefore, we should provide an easy way for *additional* checks, without by-passing the necessary validations
This leads to the following two major changes:
* The `Claims` interface now represents a set of functions that return the mandatory claims represented in a token, rather than just a `Valid` function. This is also more semantically correct.
* All validation tasks are offloaded to a new (optional) `Validator`, which can also be configured with appropriate options. If no custom validator was supplied, a default one is used.
2022-08-27 13:07:09 +03:00
|
|
|
if err := p.validator.Validate(claims); err != nil {
|
2016-06-21 23:11:54 +03:00
|
|
|
// 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 {
|
|
|
|
vErr = &ValidationError{Inner: err, Errors: ValidationErrorClaimsInvalid}
|
|
|
|
} else {
|
|
|
|
vErr = e
|
|
|
|
}
|
2015-10-29 21:45:16 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Perform validation
|
2015-11-16 23:42:37 +03:00
|
|
|
token.Signature = parts[2]
|
|
|
|
if err = token.Method.Verify(strings.Join(parts[0:2], "."), token.Signature, key); err != nil {
|
2016-04-13 03:31:30 +03:00
|
|
|
vErr.Inner = err
|
2015-10-29 21:45:16 +03:00
|
|
|
vErr.Errors |= ValidationErrorSignatureInvalid
|
|
|
|
}
|
|
|
|
|
|
|
|
if vErr.valid() {
|
|
|
|
token.Valid = true
|
|
|
|
return token, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return token, vErr
|
|
|
|
}
|
2016-07-21 19:37:13 +03:00
|
|
|
|
2021-08-03 16:51:01 +03:00
|
|
|
// ParseUnverified parses the token but doesn't validate the signature.
|
2016-07-21 19:37:13 +03:00
|
|
|
//
|
2021-08-03 16:51:01 +03:00
|
|
|
// WARNING: Don't use this method unless you know what you're doing.
|
|
|
|
//
|
|
|
|
// It's only ever useful in cases where you know the signature is valid (because it has
|
|
|
|
// been checked previously in the stack) and you want to extract values from it.
|
2016-07-21 19:37:13 +03:00
|
|
|
func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Token, parts []string, err error) {
|
|
|
|
parts = strings.Split(tokenString, ".")
|
|
|
|
if len(parts) != 3 {
|
|
|
|
return nil, parts, NewValidationError("token contains an invalid number of segments", ValidationErrorMalformed)
|
|
|
|
}
|
|
|
|
|
|
|
|
token = &Token{Raw: tokenString}
|
|
|
|
|
|
|
|
// parse Header
|
|
|
|
var headerBytes []byte
|
|
|
|
if headerBytes, err = DecodeSegment(parts[0]); err != nil {
|
|
|
|
if strings.HasPrefix(strings.ToLower(tokenString), "bearer ") {
|
|
|
|
return token, parts, NewValidationError("tokenstring should not contain 'bearer '", ValidationErrorMalformed)
|
|
|
|
}
|
|
|
|
return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
|
|
|
|
}
|
|
|
|
if err = json.Unmarshal(headerBytes, &token.Header); err != nil {
|
|
|
|
return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
|
|
|
|
}
|
|
|
|
|
|
|
|
// parse Claims
|
|
|
|
var claimBytes []byte
|
|
|
|
token.Claims = claims
|
|
|
|
|
|
|
|
if claimBytes, err = DecodeSegment(parts[1]); err != nil {
|
|
|
|
return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
|
|
|
|
}
|
|
|
|
dec := json.NewDecoder(bytes.NewBuffer(claimBytes))
|
|
|
|
if p.UseJSONNumber {
|
|
|
|
dec.UseNumber()
|
|
|
|
}
|
|
|
|
// JSON Decode. Special case for map type to avoid weird pointer behavior
|
|
|
|
if c, ok := token.Claims.(MapClaims); ok {
|
|
|
|
err = dec.Decode(&c)
|
|
|
|
} else {
|
|
|
|
err = dec.Decode(&claims)
|
|
|
|
}
|
|
|
|
// Handle decode error
|
|
|
|
if err != nil {
|
|
|
|
return token, parts, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Lookup signature method
|
|
|
|
if method, ok := token.Header["alg"].(string); ok {
|
|
|
|
if token.Method = GetSigningMethod(method); token.Method == nil {
|
|
|
|
return token, parts, NewValidationError("signing method (alg) is unavailable.", ValidationErrorUnverifiable)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return token, parts, NewValidationError("signing method (alg) is unspecified.", ValidationErrorUnverifiable)
|
|
|
|
}
|
|
|
|
|
|
|
|
return token, parts, nil
|
|
|
|
}
|