1package json
2
3import (
4	"bytes"
5	"sync"
6	"sync/atomic"
7
8	"github.com/lestrrat-go/jwx/internal/base64"
9	"github.com/pkg/errors"
10)
11
12var muGlobalConfig sync.RWMutex
13var useNumber bool
14
15// Sets the global configuration for json decoding
16func DecoderSettings(inUseNumber bool) {
17	muGlobalConfig.Lock()
18	useNumber = inUseNumber
19	muGlobalConfig.Unlock()
20}
21
22// Unmarshal respects the values specified in DecoderSettings,
23// and uses a Decoder that has certain features turned on/off
24func Unmarshal(b []byte, v interface{}) error {
25	dec := NewDecoder(bytes.NewReader(b))
26	return dec.Decode(v)
27}
28
29func AssignNextBytesToken(dst *[]byte, dec *Decoder) error {
30	var val string
31	if err := dec.Decode(&val); err != nil {
32		return errors.Wrap(err, `error reading next value`)
33	}
34
35	buf, err := base64.DecodeString(val)
36	if err != nil {
37		return errors.Errorf(`expected base64 encoded []byte (%T)`, val)
38	}
39	*dst = buf
40	return nil
41}
42
43func ReadNextStringToken(dec *Decoder) (string, error) {
44	var val string
45	if err := dec.Decode(&val); err != nil {
46		return "", errors.Wrap(err, `error reading next value`)
47	}
48	return val, nil
49}
50
51func AssignNextStringToken(dst **string, dec *Decoder) error {
52	val, err := ReadNextStringToken(dec)
53	if err != nil {
54		return err
55	}
56	*dst = &val
57	return nil
58}
59
60// FlattenAudience is a flag to specify if we should flatten the "aud"
61// entry to a string when there's only one entry.
62// In jwx < 1.1.8 we just dumped everything as an array of strings,
63// but apparently AWS Cognito doesn't handle this well.
64//
65// So now we have the ability to dump "aud" as a string if there's
66// only one entry, but we need to retain the old behavior so that
67// we don't accidentally break somebody else's code. (e.g. messing
68// up how signatures are calculated)
69var FlattenAudience uint32
70
71func EncodeAudience(enc *Encoder, aud []string) error {
72	var val interface{}
73	if len(aud) == 1 && atomic.LoadUint32(&FlattenAudience) == 1 {
74		val = aud[0]
75	} else {
76		val = aud
77	}
78	return enc.Encode(val)
79}
80
81// DecodeCtx is an interface for objects that needs that extra something
82// when decoding JSON into an object.
83type DecodeCtx interface {
84	Registry() *Registry
85}
86
87// DecodeCtxContainer is used to differentiate objects that can carry extra
88// decoding hints and those who can't.
89type DecodeCtxContainer interface {
90	DecodeCtx() DecodeCtx
91	SetDecodeCtx(DecodeCtx)
92}
93
94// stock decodeCtx. should cover 80% of the cases
95type decodeCtx struct {
96	registry *Registry
97}
98
99func NewDecodeCtx(r *Registry) DecodeCtx {
100	return &decodeCtx{registry: r}
101}
102
103func (dc *decodeCtx) Registry() *Registry {
104	return dc.registry
105}
106