Skip to content

Commit a8b39b0

Browse files
ajermakyoxisto
authored andcommitted
Revert Encoding/Decoding changes for better compatibility (golang-jwt#117)
1 parent 327371d commit a8b39b0

File tree

3 files changed

+127
-0
lines changed

3 files changed

+127
-0
lines changed

parser_test.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,10 @@ var (
2020
jwtTestRSAPrivateKey *rsa.PrivateKey
2121
jwtTestEC256PublicKey crypto.PublicKey
2222
jwtTestEC256PrivateKey crypto.PrivateKey
23+
paddedKey crypto.PublicKey
2324
defaultKeyFunc jwt.Keyfunc = func(t *jwt.Token) (interface{}, error) { return jwtTestDefaultKey, nil }
2425
ecdsaKeyFunc jwt.Keyfunc = func(t *jwt.Token) (interface{}, error) { return jwtTestEC256PublicKey, nil }
26+
paddedKeyFunc jwt.Keyfunc = func(t *jwt.Token) (interface{}, error) { return paddedKey, nil }
2527
emptyKeyFunc jwt.Keyfunc = func(t *jwt.Token) (interface{}, error) { return nil, nil }
2628
errorKeyFunc jwt.Keyfunc = func(t *jwt.Token) (interface{}, error) { return nil, errKeyFuncError }
2729
nilKeyFunc jwt.Keyfunc = nil
@@ -32,9 +34,14 @@ func init() {
3234
jwtTestDefaultKey = test.LoadRSAPublicKeyFromDisk("test/sample_key.pub")
3335
jwtTestEC256PublicKey = test.LoadECPublicKeyFromDisk("test/ec256-public.pem")
3436

37+
// Load padded public key - note there is only a public key for this key pair and should only be used for the
38+
// two test cases below.
39+
paddedKey = test.LoadECPublicKeyFromDisk("test/examplePaddedKey-public.pem")
40+
3541
// Load private keys
3642
jwtTestRSAPrivateKey = test.LoadRSAPrivateKeyFromDisk("test/sample_key")
3743
jwtTestEC256PrivateKey = test.LoadECPrivateKeyFromDisk("test/ec256-private.pem")
44+
3845
}
3946

4047
var jwtTestData = []struct {
@@ -435,6 +442,107 @@ func TestParser_ParseUnverified(t *testing.T) {
435442
}
436443
}
437444

445+
var setPaddingTestData = []struct {
446+
name string
447+
tokenString string
448+
claims jwt.Claims
449+
paddedDecode bool
450+
signingMethod jwt.SigningMethod
451+
keyfunc jwt.Keyfunc
452+
valid bool
453+
}{
454+
{
455+
name: "Validated non-padded token with padding disabled",
456+
tokenString: "",
457+
claims: jwt.MapClaims{"foo": "paddedbar"},
458+
paddedDecode: false,
459+
signingMethod: jwt.SigningMethodRS256,
460+
keyfunc: defaultKeyFunc,
461+
valid: true,
462+
},
463+
{
464+
name: "Validated non-padded token with padding enabled",
465+
tokenString: "",
466+
claims: jwt.MapClaims{"foo": "paddedbar"},
467+
paddedDecode: true,
468+
signingMethod: jwt.SigningMethodRS256,
469+
keyfunc: defaultKeyFunc,
470+
valid: true,
471+
},
472+
{
473+
name: "Error for padded token with padding disabled",
474+
tokenString: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJwYWRkZWRiYXIifQ==.20kGGJaYekGTRFf8b0TwhuETcR8lv5z2363X5jf7G1yTWVTwOmte5Ii8L8_OQbYwPoiVHmZY6iJPbt_DhCN42AeFY74BcsUhR-BVrYUVhKK0RppuzEcSlILDNeQsJDLEL035CPm1VO6Jrgk7enQPIctVxUesRgswP71OpGvJxy3j1k_J8p0WzZvRZTe1D_2Misa0UDGwnEIHhmr97fIpMSZjFxlcygQw8QN34IHLHIXMaTY1eiCf4CCr6rOS9wUeu7P3CPkmFq9XhxBT_LLCmIMhHnxP5x27FUJE_JZlfek0MmARcrhpsZS2sFhHAiWrjxjOE27jkDtv1nEwn65wMw==",
475+
claims: jwt.MapClaims{"foo": "paddedbar"},
476+
paddedDecode: false,
477+
signingMethod: jwt.SigningMethodRS256,
478+
keyfunc: defaultKeyFunc,
479+
valid: false,
480+
},
481+
{
482+
name: "Validated padded token with padding enabled",
483+
tokenString: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJwYWRkZWRiYXIifQ==.20kGGJaYekGTRFf8b0TwhuETcR8lv5z2363X5jf7G1yTWVTwOmte5Ii8L8_OQbYwPoiVHmZY6iJPbt_DhCN42AeFY74BcsUhR-BVrYUVhKK0RppuzEcSlILDNeQsJDLEL035CPm1VO6Jrgk7enQPIctVxUesRgswP71OpGvJxy3j1k_J8p0WzZvRZTe1D_2Misa0UDGwnEIHhmr97fIpMSZjFxlcygQw8QN34IHLHIXMaTY1eiCf4CCr6rOS9wUeu7P3CPkmFq9XhxBT_LLCmIMhHnxP5x27FUJE_JZlfek0MmARcrhpsZS2sFhHAiWrjxjOE27jkDtv1nEwn65wMw==",
484+
claims: jwt.MapClaims{"foo": "paddedbar"},
485+
paddedDecode: true,
486+
signingMethod: jwt.SigningMethodRS256,
487+
keyfunc: defaultKeyFunc,
488+
valid: true,
489+
},
490+
{
491+
name: "Error for example padded token with padding disabled",
492+
tokenString: "eyJ0eXAiOiJKV1QiLCJraWQiOiIxMjM0NTY3OC1hYmNkLTEyMzQtYWJjZC0xMjM0NTY3OGFiY2QiLCJhbGciOiJFUzI1NiIsImlzcyI6Imh0dHBzOi8vY29nbml0by1pZHAuZXUtd2VzdC0yLmFtYXpvbmF3cy5jb20vIiwiY2xpZW50IjoiN0xUY29QWnJWNDR6ZVg2WUs5VktBcHZPM3EiLCJzaWduZXIiOiJhcm46YXdzOmVsYXN0aWNsb2FkYmFsYW5jaW5nIiwiZXhwIjoxNjI5NDcwMTAxfQ==.eyJzdWIiOiIxMjM0NTY3OC1hYmNkLTEyMzQtYWJjZC0xMjM0NTY3OGFiY2QiLCJlbWFpbF92ZXJpZmllZCI6InRydWUiLCJlbWFpbCI6InVzZXJAZXhhbXBsZS5jb20iLCJ1c2VybmFtZSI6IjEyMzQ1Njc4LWFiY2QtMTIzNC1hYmNkLTEyMzQ1Njc4YWJjZCIsImV4cCI6MTYyOTQ3MDEwMSwiaXNzIjoiaHR0cHM6Ly9jb2duaXRvLWlkcC5ldS13ZXN0LTIuYW1hem9uYXdzLmNvbS8ifQ==.sx0muJ754glJvwWgkHaPrOI3L1gaPjRLLUvOQRk0WitnqC5Dtt1knorcbOzlEcH9zwPM2jYYIAYQz_qEyM3grw==",
493+
claims: nil,
494+
paddedDecode: false,
495+
signingMethod: jwt.SigningMethodES256,
496+
keyfunc: paddedKeyFunc,
497+
valid: false,
498+
},
499+
{
500+
name: "Validated example padded token with padding enabled",
501+
tokenString: "eyJ0eXAiOiJKV1QiLCJraWQiOiIxMjM0NTY3OC1hYmNkLTEyMzQtYWJjZC0xMjM0NTY3OGFiY2QiLCJhbGciOiJFUzI1NiIsImlzcyI6Imh0dHBzOi8vY29nbml0by1pZHAuZXUtd2VzdC0yLmFtYXpvbmF3cy5jb20vIiwiY2xpZW50IjoiN0xUY29QWnJWNDR6ZVg2WUs5VktBcHZPM3EiLCJzaWduZXIiOiJhcm46YXdzOmVsYXN0aWNsb2FkYmFsYW5jaW5nIiwiZXhwIjoxNjI5NDcwMTAxfQ==.eyJzdWIiOiIxMjM0NTY3OC1hYmNkLTEyMzQtYWJjZC0xMjM0NTY3OGFiY2QiLCJlbWFpbF92ZXJpZmllZCI6InRydWUiLCJlbWFpbCI6InVzZXJAZXhhbXBsZS5jb20iLCJ1c2VybmFtZSI6IjEyMzQ1Njc4LWFiY2QtMTIzNC1hYmNkLTEyMzQ1Njc4YWJjZCIsImV4cCI6MTYyOTQ3MDEwMSwiaXNzIjoiaHR0cHM6Ly9jb2duaXRvLWlkcC5ldS13ZXN0LTIuYW1hem9uYXdzLmNvbS8ifQ==.sx0muJ754glJvwWgkHaPrOI3L1gaPjRLLUvOQRk0WitnqC5Dtt1knorcbOzlEcH9zwPM2jYYIAYQz_qEyM3grw==",
502+
claims: nil,
503+
paddedDecode: true,
504+
signingMethod: jwt.SigningMethodES256,
505+
keyfunc: paddedKeyFunc,
506+
valid: true,
507+
},
508+
}
509+
510+
// Extension of Parsing, this is to test out functionality specific to switching codecs with padding.
511+
func TestSetPadding(t *testing.T) {
512+
for _, data := range setPaddingTestData {
513+
t.Run(data.name, func(t *testing.T) {
514+
515+
// If the token string is blank, use helper function to generate string
516+
jwt.DecodePaddingAllowed = data.paddedDecode
517+
518+
if data.tokenString == "" {
519+
data.tokenString = signToken(data.claims, data.signingMethod)
520+
521+
}
522+
523+
// Parse the token
524+
var token *jwt.Token
525+
var err error
526+
parser := new(jwt.Parser)
527+
parser.SkipClaimsValidation = true
528+
529+
// Figure out correct claims type
530+
token, err = parser.ParseWithClaims(data.tokenString, jwt.MapClaims{}, data.keyfunc)
531+
532+
if (err == nil) != data.valid || token.Valid != data.valid {
533+
t.Errorf("[%v] Error Parsing Token with decoding padding set to %v: %v",
534+
data.name,
535+
data.paddedDecode,
536+
err,
537+
)
538+
}
539+
540+
})
541+
jwt.DecodePaddingAllowed = false
542+
543+
}
544+
}
545+
438546
func BenchmarkParseUnverified(b *testing.B) {
439547

440548
// Iterate over test data set and run tests

test/examplePaddedKey-public.pem

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-----BEGIN PUBLIC KEY-----
2+
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIcaUjXhC7Mn2OonyfHF+zjblKkns
3+
4GLbILnHrZr+aQwddiff5urCDAZ177t81Mn39CDs3uhlNDxfRIRheGnK/Q==
4+
-----END PUBLIC KEY-----

token.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ import (
77
"time"
88
)
99

10+
11+
// DecodePaddingAllowed will switch the codec used for decoding JWTs respectively. Note that the JWS RFC7515
12+
// states that the tokens will utilize a Base64url encoding with no padding. Unfortunately, some implementations
13+
// of JWT are producing non-standard tokens, and thus require support for decoding. Note that this is a global
14+
// variable, and updating it will change the behavior on a package level, and is also NOT go-routine safe.
15+
// To use the non-recommended decoding, set this boolean to `true` prior to using this package.
16+
var DecodePaddingAllowed bool
17+
1018
// TimeFunc provides the current time when parsing token to validate "exp" claim (expiration time).
1119
// You can override it to use another time value. This is useful for testing or if your
1220
// server uses a different time zone than your tokens.
@@ -112,5 +120,12 @@ func EncodeSegment(seg []byte) string {
112120
// Deprecated: In a future release, we will demote this function to a non-exported function, since it
113121
// should only be used internally
114122
func DecodeSegment(seg string) ([]byte, error) {
123+
if DecodePaddingAllowed {
124+
if l := len(seg) % 4; l > 0 {
125+
seg += strings.Repeat("=", 4-l)
126+
}
127+
return base64.URLEncoding.DecodeString(seg)
128+
}
129+
115130
return base64.RawURLEncoding.DecodeString(seg)
116131
}

0 commit comments

Comments
 (0)