Skip to content
Snippets Groups Projects
Commit 2f0e9add authored by Michael Fridman's avatar Michael Fridman Committed by Christian Banse
Browse files

Backporting 0951d184 to v4

parent 7b1c1c00
No related branches found
No related tags found
No related merge requests found
package jwt
import (
"testing"
)
func TestSplitToken(t *testing.T) {
t.Parallel()
tests := []struct {
name string
input string
expected []string
isValid bool
}{
{
name: "valid token with three parts",
input: "header.claims.signature",
expected: []string{"header", "claims", "signature"},
isValid: true,
},
{
name: "invalid token with two parts only",
input: "header.claims",
expected: nil,
isValid: false,
},
{
name: "invalid token with one part only",
input: "header",
expected: nil,
isValid: false,
},
{
name: "invalid token with extra delimiter",
input: "header.claims.signature.extra",
expected: nil,
isValid: false,
},
{
name: "invalid empty token",
input: "",
expected: nil,
isValid: false,
},
{
name: "valid token with empty parts",
input: "..signature",
expected: []string{"", "", "signature"},
isValid: true,
},
{
// We are just splitting the token into parts, so we don't care about the actual values.
// It is up to the caller to validate the parts.
name: "valid token with all parts empty",
input: "..",
expected: []string{"", "", ""},
isValid: true,
},
{
name: "invalid token with just delimiters and extra part",
input: "...",
expected: nil,
isValid: false,
},
{
name: "invalid token with many delimiters",
input: "header.claims.signature..................",
expected: nil,
isValid: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
parts, ok := splitToken(tt.input)
if ok != tt.isValid {
t.Errorf("expected %t, got %t", tt.isValid, ok)
}
if ok {
for i, part := range tt.expected {
if parts[i] != part {
t.Errorf("expected %s, got %s", part, parts[i])
}
}
}
})
}
}
......@@ -7,6 +7,8 @@ import (
"strings"
)
const tokenDelimiter = "."
type Parser struct {
// If populated, only these methods will be considered valid.
//
......@@ -122,9 +124,10 @@ func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyf
// 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.
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)
var ok bool
parts, ok = splitToken(tokenString)
if !ok {
return nil, nil, NewValidationError("token contains an invalid number of segments", ValidationErrorMalformed)
}
token = &Token{Raw: tokenString}
......@@ -174,3 +177,30 @@ func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Toke
return token, parts, nil
}
// splitToken splits a token string into three parts: header, claims, and signature. It will only
// return true if the token contains exactly two delimiters and three parts. In all other cases, it
// will return nil parts and false.
func splitToken(token string) ([]string, bool) {
parts := make([]string, 3)
header, remain, ok := strings.Cut(token, tokenDelimiter)
if !ok {
return nil, false
}
parts[0] = header
claims, remain, ok := strings.Cut(remain, tokenDelimiter)
if !ok {
return nil, false
}
parts[1] = claims
// One more cut to ensure the signature is the last part of the token and there are no more
// delimiters. This avoids an issue where malicious input could contain additional delimiters
// causing unecessary overhead parsing tokens.
signature, _, unexpected := strings.Cut(remain, tokenDelimiter)
if unexpected {
return nil, false
}
parts[2] = signature
return parts, true
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment