parser.go

  1package jwt
  2
  3import (
  4	"bytes"
  5	"encoding/base64"
  6	"encoding/json"
  7	"fmt"
  8	"strings"
  9)
 10
 11const tokenDelimiter = "."
 12
 13type 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
 31func 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.
 46func (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.
 57func (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.
140func (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.
205func 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].
232func (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/
255func 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.
266func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc, options ...ParserOption) (*Token, error) {
267	return NewParser(options...).ParseWithClaims(tokenString, claims, keyFunc)
268}