Skip to content

Commit ea0051d

Browse files
committed
Support almost all base64 options
1 parent 4c1e383 commit ea0051d

File tree

8 files changed

+49
-34
lines changed

8 files changed

+49
-34
lines changed

encoder.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,21 @@ package jwt
22

33
import "io"
44

5-
// Base64Encoder is an interface that allows to implement custom Base64 encoding
6-
// algorithms.
7-
type Base64EncodeFunc func(src []byte) string
5+
type Base64Encoding interface {
6+
EncodeToString(src []byte) string
7+
DecodeString(s string) ([]byte, error)
8+
}
89

9-
// Base64Decoder is an interface that allows to implement custom Base64 decoding
10-
// algorithms.
11-
type Base64DecodeFunc func(s string) ([]byte, error)
10+
type Stricter[T Base64Encoding] interface {
11+
Strict() T
12+
}
1213

13-
// JSONEncoder is an interface that allows to implement custom JSON encoding
14-
// algorithms.
14+
// JSONMarshalFunc is an function type that allows to implement custom JSON
15+
// encoding algorithms.
1516
type JSONMarshalFunc func(v any) ([]byte, error)
1617

17-
// JSONUnmarshal is an interface that allows to implement custom JSON unmarshal
18-
// algorithms.
18+
// JSONUnmarshalFunc is an function type that allows to implement custom JSON
19+
// unmarshal algorithms.
1920
type JSONUnmarshalFunc func(data []byte, v any) error
2021

2122
type JSONDecoder interface {

errors.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ var (
2222
ErrTokenInvalidId = errors.New("token has invalid id")
2323
ErrTokenInvalidClaims = errors.New("token has invalid claims")
2424
ErrInvalidType = errors.New("invalid type for claim")
25+
ErrUnsupported = errors.New("operation is unsupported")
2526
)
2627

2728
// joinedError is an error type that works similar to what [errors.Join]

parser.go

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,11 @@ type Parser struct {
2626
type decoders struct {
2727
jsonUnmarshal JSONUnmarshalFunc
2828
jsonNewDecoder JSONNewDecoderFunc[JSONDecoder]
29-
base64Decode Base64DecodeFunc
3029

31-
// This field is disabled when using a custom base64 encoder.
32-
decodeStrict bool
30+
rawUrlBase64Encoding Base64Encoding
31+
urlBase64Encoding Base64Encoding
3332

34-
// This field is disabled when using a custom base64 encoder.
33+
decodeStrict bool
3534
decodePaddingAllowed bool
3635
}
3736

@@ -227,22 +226,35 @@ func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Toke
227226
// take into account whether the [Parser] is configured with additional options,
228227
// such as [WithStrictDecoding] or [WithPaddingAllowed].
229228
func (p *Parser) DecodeSegment(seg string) ([]byte, error) {
230-
if p.base64Decode != nil {
231-
return p.base64Decode(seg)
229+
var encoding Base64Encoding
230+
if p.rawUrlBase64Encoding != nil {
231+
encoding = p.rawUrlBase64Encoding
232+
} else {
233+
encoding = base64.RawURLEncoding
232234
}
233235

234-
encoding := base64.RawURLEncoding
235-
236236
if p.decodePaddingAllowed {
237237
if l := len(seg) % 4; l > 0 {
238238
seg += strings.Repeat("=", 4-l)
239239
}
240-
encoding = base64.URLEncoding
240+
241+
if p.urlBase64Encoding != nil {
242+
encoding = p.urlBase64Encoding
243+
} else {
244+
encoding = base64.URLEncoding
245+
}
241246
}
242247

243248
if p.decodeStrict {
244-
encoding = encoding.Strict()
249+
// For now we can only support the standard library here because of the
250+
// current state of the type parameter system
251+
stricter, ok := encoding.(Stricter[*base64.Encoding])
252+
if !ok {
253+
return nil, newError("strict mode is only supported in encoding/base64", ErrUnsupported)
254+
}
255+
encoding = stricter.Strict()
245256
}
257+
246258
return encoding.DecodeString(seg)
247259
}
248260

parser_option.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,10 @@ func WithJSONDecoder[T JSONDecoder](f JSONUnmarshalFunc, f2 JSONNewDecoderFunc[T
134134
}
135135
}
136136

137-
// WithBase64Decoder supports a custom [Base64Decoder] to use in parsing the JWT.
138-
func WithBase64Decoder(f Base64DecodeFunc) ParserOption {
137+
// WithBase64Decoder supports a custom [Base64Encoding] to use in parsing the JWT.
138+
func WithBase64Decoder(rawURL Base64Encoding, url Base64Encoding) ParserOption {
139139
return func(p *Parser) {
140-
p.base64Decode = f
140+
p.rawUrlBase64Encoding = rawURL
141+
p.urlBase64Encoding = url
141142
}
142143
}

parser_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ var jwtTestData = []struct {
454454
jwt.MapClaims{"foo": "bar"},
455455
true,
456456
nil,
457-
jwt.NewParser(jwt.WithBase64Decoder(base64.RawURLEncoding.DecodeString)),
457+
jwt.NewParser(jwt.WithBase64Decoder(base64.RawURLEncoding, base64.URLEncoding)),
458458
jwt.SigningMethodRS256,
459459
},
460460
}

token.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ type Token struct {
3939
}
4040

4141
type encoders struct {
42-
jsonMarshal JSONMarshalFunc // jsonEncoder is the custom json encoder/decoder
43-
base64Encode Base64EncodeFunc // base64Encoder is the custom base64 encoder/decoder
42+
jsonMarshal JSONMarshalFunc // jsonEncoder is the custom json encoder/decoder
43+
base64Encoding Base64Encoding // base64Encoder is the custom base64 encoding
4444
}
4545

4646
// New creates a new [Token] with the specified signing method and an empty map
@@ -114,12 +114,12 @@ func (t *Token) SigningString() (string, error) {
114114
// [TokenOption]. Therefore, this function exists as a method of [Token], rather
115115
// than a global function.
116116
func (t *Token) EncodeSegment(seg []byte) string {
117-
var enc Base64EncodeFunc
118-
if t.base64Encode != nil {
119-
enc = t.base64Encode
117+
var enc Base64Encoding
118+
if t.base64Encoding != nil {
119+
enc = t.base64Encoding
120120
} else {
121-
enc = base64.RawURLEncoding.EncodeToString
121+
enc = base64.RawURLEncoding
122122
}
123123

124-
return enc(seg)
124+
return enc.EncodeToString(seg)
125125
}

token_option.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ func WithJSONEncoder(f JSONMarshalFunc) TokenOption {
1010
}
1111
}
1212

13-
func WithBase64Encoder(f Base64EncodeFunc) TokenOption {
13+
func WithBase64Encoder(enc Base64Encoding) TokenOption {
1414
return func(token *Token) {
15-
token.base64Encode = f
15+
token.base64Encoding = enc
1616
}
1717
}

token_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func TestToken_SigningString(t1 *testing.T) {
5353
Valid: false,
5454
Options: []jwt.TokenOption{
5555
jwt.WithJSONEncoder(json.Marshal),
56-
jwt.WithBase64Encoder(base64.StdEncoding.EncodeToString),
56+
jwt.WithBase64Encoder(base64.StdEncoding),
5757
},
5858
},
5959
want: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30",

0 commit comments

Comments
 (0)