Skip to content

Commit 4c1e383

Browse files
committed
Also supports UseNumber
1 parent d81e689 commit 4c1e383

File tree

4 files changed

+51
-49
lines changed

4 files changed

+51
-49
lines changed

encoder.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package jwt
22

3+
import "io"
4+
35
// Base64Encoder is an interface that allows to implement custom Base64 encoding
46
// algorithms.
57
type Base64EncodeFunc func(src []byte) string
@@ -15,3 +17,10 @@ type JSONMarshalFunc func(v any) ([]byte, error)
1517
// JSONUnmarshal is an interface that allows to implement custom JSON unmarshal
1618
// algorithms.
1719
type JSONUnmarshalFunc func(data []byte, v any) error
20+
21+
type JSONDecoder interface {
22+
UseNumber()
23+
Decode(v any) error
24+
}
25+
26+
type JSONNewDecoderFunc[T JSONDecoder] func(r io.Reader) T

parser.go

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ 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. This field is disabled when using a custom json encoder.
15+
// Use JSON Number format in JSON decoder.
1616
useJSONNumber bool
1717

1818
// Skip claims validation during token parsing.
@@ -24,8 +24,9 @@ type Parser struct {
2424
}
2525

2626
type decoders struct {
27-
jsonUnmarshal JSONUnmarshalFunc
28-
base64Decode Base64DecodeFunc
27+
jsonUnmarshal JSONUnmarshalFunc
28+
jsonNewDecoder JSONNewDecoderFunc[JSONDecoder]
29+
base64Decode Base64DecodeFunc
2930

3031
// This field is disabled when using a custom base64 encoder.
3132
decodeStrict bool
@@ -180,12 +181,20 @@ func (p *Parser) ParseUnverified(tokenString string, claims Claims) (token *Toke
180181
return token, parts, newError("could not base64 decode claim", ErrTokenMalformed, err)
181182
}
182183

183-
// If `useJSONNumber` is enabled, then we must use *json.Decoder to decode
184-
// the claims. However, this comes with a performance penalty so only use
185-
// it if we must and, otherwise, simple use our existing unmarshal function.
184+
// If `useJSONNumber` is enabled, then we must use a dedicated JSONDecoder
185+
// to decode the claims. However, this comes with a performance penalty so
186+
// only use it if we must and, otherwise, simple use our existing unmarshal
187+
// function.
186188
if p.useJSONNumber {
187189
unmarshal = func(data []byte, v any) error {
188-
decoder := json.NewDecoder(bytes.NewBuffer(claimBytes))
190+
buffer := bytes.NewBuffer(claimBytes)
191+
192+
var decoder JSONDecoder
193+
if p.jsonNewDecoder != nil {
194+
decoder = p.jsonNewDecoder(buffer)
195+
} else {
196+
decoder = json.NewDecoder(buffer)
197+
}
189198
decoder.UseNumber()
190199
return decoder.Decode(v)
191200
}

parser_option.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package jwt
22

3-
import "time"
3+
import (
4+
"io"
5+
"time"
6+
)
47

58
// ParserOption is used to implement functional-style options that modify the
69
// behavior of the parser. To add new options, just create a function (ideally
@@ -119,10 +122,15 @@ func WithStrictDecoding() ParserOption {
119122
}
120123
}
121124

122-
// WithJSONUnmarshal supports a custom [JSONUnmarshal] to use in parsing the JWT.
123-
func WithJSONUnmarshal(f JSONUnmarshalFunc) ParserOption {
125+
// WithJSONDecoder supports a custom [JSONUnmarshal] to use in parsing the JWT.
126+
func WithJSONDecoder[T JSONDecoder](f JSONUnmarshalFunc, f2 JSONNewDecoderFunc[T]) ParserOption {
124127
return func(p *Parser) {
125128
p.jsonUnmarshal = f
129+
// This seems to be necessary, since we don't want to store the specific
130+
// JSONDecoder type in our parser, but need it in the function interface.
131+
p.jsonNewDecoder = func(r io.Reader) JSONDecoder {
132+
return f2(r)
133+
}
126134
}
127135
}
128136

0 commit comments

Comments
 (0)