Skip to content

Commit cd88b49

Browse files
author
zhouyiheng.go
committed
feat: custom json and base64 encoders for Token and Parser
1 parent b88a60f commit cd88b49

9 files changed

+230
-21
lines changed

encoder.go

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package jwt
2+
3+
// Base64Encoder is an interface that allows to implement custom Base64 encoding/decoding algorithms.
4+
type Base64Encoder interface {
5+
EncodeToString(src []byte) string
6+
DecodeString(s string) ([]byte, error)
7+
}
8+
9+
// JSONEncoder is an interface that allows to implement custom JSON encoding/decoding algorithms.
10+
type JSONEncoder interface {
11+
Marshal(v any) ([]byte, error)
12+
Unmarshal(data []byte, v any) error
13+
}

encoder_test.go

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package jwt_test
2+
3+
import (
4+
"encoding/base64"
5+
"encoding/json"
6+
)
7+
8+
type customJSONEncoder struct{}
9+
10+
func (s *customJSONEncoder) Marshal(v any) ([]byte, error) {
11+
return json.Marshal(v)
12+
}
13+
14+
func (s *customJSONEncoder) Unmarshal(data []byte, v any) error {
15+
return json.Unmarshal(data, v)
16+
}
17+
18+
type customBase64Encoder struct{}
19+
20+
func (s *customBase64Encoder) EncodeToString(data []byte) string {
21+
return base64.StdEncoding.EncodeToString(data)
22+
}
23+
24+
func (s *customBase64Encoder) DecodeString(data string) ([]byte, error) {
25+
return base64.RawURLEncoding.DecodeString(data)
26+
}

go.sum

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
2+
github.com/bytedance/sonic v1.8.6 h1:aUgO9S8gvdN6SyW2EhIpAw5E4ChworywIEndZCkCVXk=
3+
github.com/bytedance/sonic v1.8.6/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
4+
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
5+
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
6+
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
7+
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
9+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
10+
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
11+
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
12+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
13+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
14+
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
15+
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
16+
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
17+
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
18+
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
19+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
20+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
21+
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
22+
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
23+
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
24+
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
25+
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
26+
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=
27+
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
28+
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
29+
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
30+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
31+
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
32+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
33+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
34+
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

parser.go

+36-10
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,25 @@ type Parser struct {
1212
// If populated, only these methods will be considered valid.
1313
validMethods []string
1414

15-
// Use JSON Number format in JSON decoder.
15+
// Use JSON Number format in JSON decoder. This field is disabled when using a custom json encoder.
1616
useJSONNumber bool
1717

1818
// Skip claims validation during token parsing.
1919
skipClaimsValidation bool
2020

2121
validator *validator
2222

23+
// This field is disabled when using a custom base64 encoder.
2324
decodeStrict bool
2425

26+
// This field is disabled when using a custom base64 encoder.
2527
decodePaddingAllowed bool
28+
29+
// Custom base64 encoder.
30+
base64Encoder Base64Encoder
31+
32+
// Custom json encoder.
33+
jsonEncoder JSONEncoder
2634
}
2735

2836
// NewParser creates a new Parser with the specified options
@@ -135,7 +143,12 @@ func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Toke
135143
}
136144
return token, parts, newError("could not base64 decode header", ErrTokenMalformed, err)
137145
}
138-
if err = json.Unmarshal(headerBytes, &token.Header); err != nil {
146+
if p.jsonEncoder != nil {
147+
err = p.jsonEncoder.Unmarshal(headerBytes, &token.Header)
148+
} else {
149+
err = json.Unmarshal(headerBytes, &token.Header)
150+
}
151+
if err != nil {
139152
return token, parts, newError("could not JSON decode header", ErrTokenMalformed, err)
140153
}
141154

@@ -146,21 +159,30 @@ func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Toke
146159
if claimBytes, err = p.DecodeSegment(parts[1]); err != nil {
147160
return token, parts, newError("could not base64 decode claim", ErrTokenMalformed, err)
148161
}
149-
dec := json.NewDecoder(bytes.NewBuffer(claimBytes))
150-
if p.useJSONNumber {
151-
dec.UseNumber()
152-
}
162+
153163
// JSON Decode. Special case for map type to avoid weird pointer behavior
154-
if c, ok := token.Claims.(MapClaims); ok {
155-
err = dec.Decode(&c)
164+
mapClaims, isMapClaims := token.Claims.(MapClaims)
165+
if p.jsonEncoder != nil {
166+
if isMapClaims {
167+
err = p.jsonEncoder.Unmarshal(claimBytes, &mapClaims)
168+
} else {
169+
err = p.jsonEncoder.Unmarshal(claimBytes, &claims)
170+
}
156171
} else {
157-
err = dec.Decode(&claims)
172+
decoder := json.NewDecoder(bytes.NewBuffer(claimBytes))
173+
if p.useJSONNumber {
174+
decoder.UseNumber()
175+
}
176+
if isMapClaims {
177+
err = decoder.Decode(&mapClaims)
178+
} else {
179+
err = decoder.Decode(&claims)
180+
}
158181
}
159182
// Handle decode error
160183
if err != nil {
161184
return token, parts, newError("could not JSON decode claim", ErrTokenMalformed, err)
162185
}
163-
164186
// Lookup signature method
165187
if method, ok := token.Header["alg"].(string); ok {
166188
if token.Method = GetSigningMethod(method); token.Method == nil {
@@ -177,6 +199,10 @@ func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Toke
177199
// take into account whether the [Parser] is configured with additional options,
178200
// such as [WithStrictDecoding] or [WithPaddingAllowed].
179201
func (p *Parser) DecodeSegment(seg string) ([]byte, error) {
202+
if p.base64Encoder != nil {
203+
return p.base64Encoder.DecodeString(seg)
204+
}
205+
180206
encoding := base64.RawURLEncoding
181207

182208
if p.decodePaddingAllowed {

parser_option.go

+13
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,16 @@ func WithStrictDecoding() ParserOption {
125125
p.decodeStrict = true
126126
}
127127
}
128+
129+
// WithJSONEncoder supports
130+
func WithJSONEncoder(enc JSONEncoder) ParserOption {
131+
return func(p *Parser) {
132+
p.jsonEncoder = enc
133+
}
134+
}
135+
136+
func WithBase64Encoder(enc Base64Encoder) ParserOption {
137+
return func(p *Parser) {
138+
p.base64Encoder = enc
139+
}
140+
}

0 commit comments

Comments
 (0)