-
-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathtoken.go
234 lines (190 loc) · 6.75 KB
/
token.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
package paseto
import (
"encoding/json"
"fmt"
"time"
t "aidanwoods.dev/go-result"
)
// Token is a set of paseto claims, and a footer
type Token struct {
claims map[string]json.RawMessage
footer []byte
}
func StdDecoder(caf TokenClaimsAndFooter) (*Token, error) {
return NewTokenFromClaimsJSON(caf.Claims, caf.Footer)
}
// NewToken returns a token with no claims and no footer.
func NewToken() Token {
return Token{make(map[string]json.RawMessage), nil}
}
func makeToken(claims map[string]json.RawMessage, footer []byte) (*Token, error) {
tokenValueClaims := make(map[string]json.RawMessage)
token := Token{tokenValueClaims, footer}
for key, value := range claims {
token.claims[key] = value
}
return &token, nil
}
// MakeToken allows specifying both claims and a footer.
func MakeToken(claims map[string]interface{}, footer []byte) (*Token, error) {
tokenValueClaims := make(map[string]json.RawMessage)
token := Token{tokenValueClaims, footer}
for key, value := range claims {
if err := token.Set(key, value); err != nil {
return nil, err
}
}
return &token, nil
}
// NewTokenFromClaimsJSON parses the JSON using encoding/json in claimsData
// and returns a token with those claims, and the specified footer.
func NewTokenFromClaimsJSON(claimsData []byte, footer []byte) (*Token, error) {
var claims map[string]json.RawMessage
if err := json.Unmarshal(claimsData, &claims); err != nil {
return nil, err
}
return makeToken(claims, footer)
}
// Set sets the key with the specified value. Note that this value needs to
// be serialisable to JSON using encoding/json.
// Set will check this and return an error if it is not serialisable.
func (token *Token) Set(key string, value any) error {
return t.Chain[any](
marshalTokenValue(value)).
AndThen(func(value json.RawMessage) t.Result[any] {
token.claims[key] = value
return t.Ok[any](nil)
}).
WrapErr("could not set key `" + key + "': %w").
UnwrapErrOr(nil)
}
// Get gets the given key and writes the value into output (which should be a
// a pointer), if present by parsing the JSON using encoding/json.
func (t Token) Get(key string, output any) (err error) {
v, ok := t.claims[key]
if !ok {
return fmt.Errorf("value for key `%s' not present in claims", key)
}
if err := json.Unmarshal(v, &output); err != nil {
output = nil
return err
}
return nil
}
func Get[T any](token Token, key string) t.Result[T] {
var out T
if err := token.Get(key, &out); err != nil {
return t.Err[T](err)
}
return t.Ok(out)
}
// GetString returns the value for a given key as a string, or error if this
// is not possible (cannot be a string, or value does not exist)
func (t Token) GetString(key string) (string, error) {
var str string
if err := t.Get(key, &str); err != nil {
return "", err
}
return str, nil
}
// SetString sets the given key with value. If, for some reason, the provided
// string cannot be serialised as JSON SetString will panic.
func (t *Token) SetString(key string, value string) {
if err := t.Set(key, value); err != nil {
// panic if we get an error, we shouldn't fail to encode a string value
panic(err)
}
}
// GetTime returns the time for a given key as a string, or error if this
// is not possible (cannot parse as a time, or value does not exist)
func (t Token) GetTime(key string) (time.Time, error) {
timeStr, err := t.GetString(key)
if err != nil {
return time.Time{}, err
}
return time.Parse(time.RFC3339, timeStr)
}
// SetTime sets the given key with the given time, encoded using RFC3339 (the
// time format used by common PASETO claims).
func (t *Token) SetTime(key string, value time.Time) {
t.SetString(key, value.Format(time.RFC3339))
}
// Claims gets the stored claims.
func (t Token) Claims() map[string]interface{} {
claims := make(map[string]interface{})
for key, value := range t.claims {
var claimValue interface{}
if err := json.Unmarshal(value, &claimValue); err != nil {
// we only store claims that have gone through json.Marshal
// it is *very* unexpected if this is not reversable
panic(err)
}
claims[key] = claimValue
}
return claims
}
// ClaimsJSON gets the stored claims as JSON.
func (token Token) ClaimsJSON() []byte {
// these were *just* unmarshalled (and a top level of string keys added)
// it is *very* unexpected if this is not reversable
data := t.NewResult(json.Marshal(token.claims)).
Expect("internal claims data should be well formed JSON")
return data
}
// Footer returns the token's footer
func (t Token) Footer() []byte {
return t.footer
}
// SetFooter sets the token's footer
func (t *Token) SetFooter(footer []byte) {
t.footer = footer
}
func (t Token) encode() TokenClaimsAndFooter {
return TokenClaimsAndFooter{t.ClaimsJSON(), []byte(t.footer)}
}
// V2Sign signs the token, using the given key.
func (t Token) V2Sign(key V2AsymmetricSecretKey) string {
return t.encode().V2Sign(key)
}
// V2Encrypt signs the token, using the given key.
func (t Token) V2Encrypt(key V2SymmetricKey) string {
return t.encode().V2Encrypt(key)
}
// V3Sign signs the token, using the given key and implicit bytes. Implicit
// bytes are bytes used to calculate the signature, but which are not present in
// the final token.
// Implicit must be reprovided for successful verification, and can not be
// recovered.
func (t Token) V3Sign(key V3AsymmetricSecretKey, implicit []byte) string {
return t.encode().V3Sign(key, implicit)
}
// V3Encrypt signs the token, using the given key and implicit bytes. Implicit
// bytes are bytes used to calculate the encrypted token, but which are not
// present in the final token (or its decrypted value).
// Implicit must be reprovided for successful decryption, and can not be
// recovered.
func (t Token) V3Encrypt(key V3SymmetricKey, implicit []byte) string {
return t.encode().V3Encrypt(key, implicit)
}
// V4Sign signs the token, using the given key and implicit bytes. Implicit
// bytes are bytes used to calculate the signature, but which are not present in
// the final token.
// Implicit must be reprovided for successful verification, and can not be
// recovered.
func (t Token) V4Sign(key V4AsymmetricSecretKey, implicit []byte) string {
return t.encode().V4Sign(key, implicit)
}
// V4Encrypt signs the token, using the given key and implicit bytes. Implicit
// bytes are bytes used to calculate the encrypted token, but which are not
// present in the final token (or its decrypted value).
// Implicit must be reprovided for successful decryption, and can not be
// recovered.
func (t Token) V4Encrypt(key V4SymmetricKey, implicit []byte) string {
return t.encode().V4Encrypt(key, implicit)
}
func newTokenValue(bytes []byte) json.RawMessage {
return json.RawMessage(bytes)
}
func marshalTokenValue(value interface{}) t.Result[json.RawMessage] {
return t.Map(t.NewResult(json.Marshal(value)), newTokenValue)
}