parser.go raw

   1  package jwt
   2  
   3  import (
   4  	"bytes"
   5  	"encoding/base64"
   6  	"encoding/json"
   7  	"fmt"
   8  	"strings"
   9  )
  10  
  11  const tokenDelimiter = "."
  12  
  13  type Parser struct {
  14  	// If populated, only these methods will be considered valid.
  15  	validMethods []string
  16  
  17  	// Use JSON Number format in JSON decoder.
  18  	useJSONNumber bool
  19  
  20  	// Skip claims validation during token parsing.
  21  	skipClaimsValidation bool
  22  
  23  	validator *Validator
  24  
  25  	decodeStrict bool
  26  
  27  	decodePaddingAllowed bool
  28  }
  29  
  30  // NewParser creates a new Parser with the specified options
  31  func NewParser(options ...ParserOption) *Parser {
  32  	p := &Parser{
  33  		validator: &Validator{},
  34  	}
  35  
  36  	// Loop through our parsing options and apply them
  37  	for _, option := range options {
  38  		option(p)
  39  	}
  40  
  41  	return p
  42  }
  43  
  44  // Parse parses, validates, verifies the signature and returns the parsed token.
  45  // keyFunc will receive the parsed token and should return the key for validating.
  46  func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) {
  47  	return p.ParseWithClaims(tokenString, MapClaims{}, keyFunc)
  48  }
  49  
  50  // ParseWithClaims parses, validates, and verifies like Parse, but supplies a default object implementing the Claims
  51  // interface. This provides default values which can be overridden and allows a caller to use their own type, rather
  52  // than the default MapClaims implementation of Claims.
  53  //
  54  // Note: If you provide a custom claim implementation that embeds one of the standard claims (such as RegisteredClaims),
  55  // make sure that a) you either embed a non-pointer version of the claims or b) if you are using a pointer, allocate the
  56  // proper memory for it before passing in the overall claims, otherwise you might run into a panic.
  57  func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) {
  58  	token, parts, err := p.ParseUnverified(tokenString, claims)
  59  	if err != nil {
  60  		return token, err
  61  	}
  62  
  63  	// Verify signing method is in the required set
  64  	if p.validMethods != nil {
  65  		var signingMethodValid = false
  66  		var alg = token.Method.Alg()
  67  		for _, m := range p.validMethods {
  68  			if m == alg {
  69  				signingMethodValid = true
  70  				break
  71  			}
  72  		}
  73  		if !signingMethodValid {
  74  			// signing method is not in the listed set
  75  			return token, newError(fmt.Sprintf("signing method %v is invalid", alg), ErrTokenSignatureInvalid)
  76  		}
  77  	}
  78  
  79  	// Decode signature
  80  	token.Signature, err = p.DecodeSegment(parts[2])
  81  	if err != nil {
  82  		return token, newError("could not base64 decode signature", ErrTokenMalformed, err)
  83  	}
  84  	text := strings.Join(parts[0:2], ".")
  85  
  86  	// Lookup key(s)
  87  	if keyFunc == nil {
  88  		// keyFunc was not provided.  short circuiting validation
  89  		return token, newError("no keyfunc was provided", ErrTokenUnverifiable)
  90  	}
  91  
  92  	got, err := keyFunc(token)
  93  	if err != nil {
  94  		return token, newError("error while executing keyfunc", ErrTokenUnverifiable, err)
  95  	}
  96  
  97  	switch have := got.(type) {
  98  	case VerificationKeySet:
  99  		if len(have.Keys) == 0 {
 100  			return token, newError("keyfunc returned empty verification key set", ErrTokenUnverifiable)
 101  		}
 102  		// Iterate through keys and verify signature, skipping the rest when a match is found.
 103  		// Return the last error if no match is found.
 104  		for _, key := range have.Keys {
 105  			if err = token.Method.Verify(text, token.Signature, key); err == nil {
 106  				break
 107  			}
 108  		}
 109  	default:
 110  		err = token.Method.Verify(text, token.Signature, have)
 111  	}
 112  	if err != nil {
 113  		return token, newError("", ErrTokenSignatureInvalid, err)
 114  	}
 115  
 116  	// Validate Claims
 117  	if !p.skipClaimsValidation {
 118  		// Make sure we have at least a default validator
 119  		if p.validator == nil {
 120  			p.validator = NewValidator()
 121  		}
 122  
 123  		if err := p.validator.Validate(claims); err != nil {
 124  			return token, newError("", ErrTokenInvalidClaims, err)
 125  		}
 126  	}
 127  
 128  	// No errors so far, token is valid.
 129  	token.Valid = true
 130  
 131  	return token, nil
 132  }
 133  
 134  // ParseUnverified parses the token but doesn't validate the signature.
 135  //
 136  // WARNING: Don't use this method unless you know what you're doing.
 137  //
 138  // It's only ever useful in cases where you know the signature is valid (since it has already
 139  // been or will be checked elsewhere in the stack) and you want to extract values from it.
 140  func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Token, parts []string, err error) {
 141  	var ok bool
 142  	parts, ok = splitToken(tokenString)
 143  	if !ok {
 144  		return nil, nil, newError("token contains an invalid number of segments", ErrTokenMalformed)
 145  	}
 146  
 147  	token = &Token{Raw: tokenString}
 148  
 149  	// parse Header
 150  	var headerBytes []byte
 151  	if headerBytes, err = p.DecodeSegment(parts[0]); err != nil {
 152  		return token, parts, newError("could not base64 decode header", ErrTokenMalformed, err)
 153  	}
 154  	if err = json.Unmarshal(headerBytes, &token.Header); err != nil {
 155  		return token, parts, newError("could not JSON decode header", ErrTokenMalformed, err)
 156  	}
 157  
 158  	// parse Claims
 159  	token.Claims = claims
 160  
 161  	claimBytes, err := p.DecodeSegment(parts[1])
 162  	if err != nil {
 163  		return token, parts, newError("could not base64 decode claim", ErrTokenMalformed, err)
 164  	}
 165  
 166  	// If `useJSONNumber` is enabled then we must use *json.Decoder to decode
 167  	// the claims. However, this comes with a performance penalty so only use
 168  	// it if we must and, otherwise, simple use json.Unmarshal.
 169  	if !p.useJSONNumber {
 170  		// JSON Unmarshal. Special case for map type to avoid weird pointer behavior.
 171  		if c, ok := token.Claims.(MapClaims); ok {
 172  			err = json.Unmarshal(claimBytes, &c)
 173  		} else {
 174  			err = json.Unmarshal(claimBytes, &claims)
 175  		}
 176  	} else {
 177  		dec := json.NewDecoder(bytes.NewBuffer(claimBytes))
 178  		dec.UseNumber()
 179  		// JSON Decode. Special case for map type to avoid weird pointer behavior.
 180  		if c, ok := token.Claims.(MapClaims); ok {
 181  			err = dec.Decode(&c)
 182  		} else {
 183  			err = dec.Decode(&claims)
 184  		}
 185  	}
 186  	if err != nil {
 187  		return token, parts, newError("could not JSON decode claim", ErrTokenMalformed, err)
 188  	}
 189  
 190  	// Lookup signature method
 191  	if method, ok := token.Header["alg"].(string); ok {
 192  		if token.Method = GetSigningMethod(method); token.Method == nil {
 193  			return token, parts, newError("signing method (alg) is unavailable", ErrTokenUnverifiable)
 194  		}
 195  	} else {
 196  		return token, parts, newError("signing method (alg) is unspecified", ErrTokenUnverifiable)
 197  	}
 198  
 199  	return token, parts, nil
 200  }
 201  
 202  // splitToken splits a token string into three parts: header, claims, and signature. It will only
 203  // return true if the token contains exactly two delimiters and three parts. In all other cases, it
 204  // will return nil parts and false.
 205  func splitToken(token string) ([]string, bool) {
 206  	parts := make([]string, 3)
 207  	header, remain, ok := strings.Cut(token, tokenDelimiter)
 208  	if !ok {
 209  		return nil, false
 210  	}
 211  	parts[0] = header
 212  	claims, remain, ok := strings.Cut(remain, tokenDelimiter)
 213  	if !ok {
 214  		return nil, false
 215  	}
 216  	parts[1] = claims
 217  	// One more cut to ensure the signature is the last part of the token and there are no more
 218  	// delimiters. This avoids an issue where malicious input could contain additional delimiters
 219  	// causing unecessary overhead parsing tokens.
 220  	signature, _, unexpected := strings.Cut(remain, tokenDelimiter)
 221  	if unexpected {
 222  		return nil, false
 223  	}
 224  	parts[2] = signature
 225  
 226  	return parts, true
 227  }
 228  
 229  // DecodeSegment decodes a JWT specific base64url encoding. This function will
 230  // take into account whether the [Parser] is configured with additional options,
 231  // such as [WithStrictDecoding] or [WithPaddingAllowed].
 232  func (p *Parser) DecodeSegment(seg string) ([]byte, error) {
 233  	encoding := base64.RawURLEncoding
 234  
 235  	if p.decodePaddingAllowed {
 236  		if l := len(seg) % 4; l > 0 {
 237  			seg += strings.Repeat("=", 4-l)
 238  		}
 239  		encoding = base64.URLEncoding
 240  	}
 241  
 242  	if p.decodeStrict {
 243  		encoding = encoding.Strict()
 244  	}
 245  	return encoding.DecodeString(seg)
 246  }
 247  
 248  // Parse parses, validates, verifies the signature and returns the parsed token.
 249  // keyFunc will receive the parsed token and should return the cryptographic key
 250  // for verifying the signature. The caller is strongly encouraged to set the
 251  // WithValidMethods option to validate the 'alg' claim in the token matches the
 252  // expected algorithm. For more details about the importance of validating the
 253  // 'alg' claim, see
 254  // https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/
 255  func Parse(tokenString string, keyFunc Keyfunc, options ...ParserOption) (*Token, error) {
 256  	return NewParser(options...).Parse(tokenString, keyFunc)
 257  }
 258  
 259  // ParseWithClaims is a shortcut for NewParser().ParseWithClaims().
 260  //
 261  // Note: If you provide a custom claim implementation that embeds one of the
 262  // standard claims (such as RegisteredClaims), make sure that a) you either
 263  // embed a non-pointer version of the claims or b) if you are using a pointer,
 264  // allocate the proper memory for it before passing in the overall claims,
 265  // otherwise you might run into a panic.
 266  func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc, options ...ParserOption) (*Token, error) {
 267  	return NewParser(options...).ParseWithClaims(tokenString, claims, keyFunc)
 268  }
 269