-
Notifications
You must be signed in to change notification settings - Fork 5
/
jwt.go
205 lines (197 loc) · 5.7 KB
/
jwt.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
// Package jwt provides a simplified and secure API for encoding, decoding and
// verifying JSON Web Tokens (JWT).
//
// See https://jwt.io/ and https://tools.ietf.org/html/rfc7519
//
// The API is designed to be instantly familiar to users of the standard crypto
// and json packages:
//
// // create jwt.Signer from a key store
// rs384 := jwt.RS384.New(myKeyStore())
//
// // create claims
// claims := jwt.Claims{
// Issuer: "[email protected]",
// }
//
// // encode claims as a JWT:
// buf, err := rs384.Encode(&claims)
// if err != nil {
// log.Fatalln(err)
// }
// fmt.Printf("token: %s\n", string(buf))
//
// // decode and verify claims:
// cl2 := jwt.Claims{}
// err = rs384.Decode(buf, &cl2)
// if err == jwt.ErrInvalidSignature {
// // invalid signature
// } else if err != nil {
// // handle general error
// }
//
// fmt.Printf("decoded claims: %+v\n", cl2)
//
package jwt
import (
"bytes"
"encoding/json"
"reflect"
)
// tokenSep is the token separator.
var tokenSep = []byte{'.'}
// Decode decodes a serialized JWT in buf into obj, and verifies the JWT
// signature using the Algorithm and Signer.
//
// If the token or signature is invalid, ErrInvalidToken or ErrInvalidSignature
// will be returned, respectively. Otherwise, any other errors encountered
// during token decoding will be returned.
func Decode(alg Algorithm, signer Signer, buf []byte, obj interface{}) error {
// split token
ut := UnverifiedToken{}
if err := DecodeUnverifiedToken(buf, &ut); err != nil {
return err
}
// verify signature
sig, err := signer.Verify(buf[:len(ut.Header)+len(tokenSep)+len(ut.Payload)], ut.Signature)
if err != nil {
return ErrInvalidSignature
}
// b64 decode header
headerBuf, err := b64.DecodeString(string(ut.Header))
if err != nil {
return err
}
// json decode header
header := Header{}
if err = json.Unmarshal(headerBuf, &header); err != nil {
return err
}
// verify alg matches header algorithm
if alg != header.Algorithm {
return ErrInvalidAlgorithm
}
// set header in the provided obj
if err := decodeToObjOrFieldWithTag(headerBuf, obj, "header", &header); err != nil {
return err
}
// b64 decode payload
payloadBuf, err := b64.DecodeString(string(ut.Payload))
if err != nil {
return err
}
// json decode payload
payload := Claims{}
if err := json.Unmarshal(payloadBuf, &payload); err != nil {
return err
}
// set payload in the provided obj
if err := decodeToObjOrFieldWithTag(payloadBuf, obj, "payload", &payload); err != nil {
return err
}
// set sig in the provided obj
field := getFieldWithTag(obj, "signature")
if field != nil {
field.Set(reflect.ValueOf(sig))
}
return nil
}
// Encode encodes a JWT using the Algorithm and Signer, returning the URL-safe
// encoded token or any errors encountered during encoding.
func Encode(alg Algorithm, signer Signer, obj interface{}) ([]byte, error) {
// grab encode targets
headerObj, payloadObj, err := encodeTargets(alg, obj)
if err != nil {
return nil, err
}
// json encode header
header, err := json.Marshal(headerObj)
if err != nil {
return nil, err
}
// b64 encode playload
headerEnc := make([]byte, b64.EncodedLen(len(header)))
b64.Encode(headerEnc, header)
// json encode payload
payload, err := json.Marshal(payloadObj)
if err != nil {
return nil, err
}
// b64 encode playload
payloadEnc := make([]byte, b64.EncodedLen(len(payload)))
b64.Encode(payloadEnc, payload)
// add header
var buf bytes.Buffer
if _, err = buf.Write(headerEnc); err != nil {
return nil, err
}
// add 1st separator
if _, err = buf.Write(tokenSep); err != nil {
return nil, err
}
// add payload
if _, err = buf.Write(payloadEnc); err != nil {
return nil, err
}
// sign
sig, err := signer.Sign(buf.Bytes())
if err != nil {
return nil, err
}
// add 2nd separator
if _, err = buf.Write(tokenSep); err != nil {
return nil, err
}
// add sig
if _, err = buf.Write(sig); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// PeekHeaderField extracts the specified field from the serialized JWT buf's
// header. An error will be returned if the field is not present in the decoded
// header.
func PeekHeaderField(buf []byte, field string) (string, error) {
return peekField(buf, field, tokenPositionHeader)
}
// PeekPayloadField extracts the specified field from the serialized JWT buf's
// payload (ie, the token claims). An error will be returned if the field is
// not present in the decoded payload.
func PeekPayloadField(buf []byte, field string) (string, error) {
return peekField(buf, field, tokenPositionPayload)
}
// PeekAlgorithm extracts the signing algorithm listed in the "alg" field of
// the serialized JWT buf's header and attempts to unmarshal it into an
// Algorithm. An error will be returned if the alg field is not specified in
// the JWT header, or is otherwise invalid.
func PeekAlgorithm(buf []byte) (Algorithm, error) {
alg := NONE
// get alg
algVal, err := PeekHeaderField(buf, "alg")
if err != nil {
return NONE, err
}
// decode alg
if err = (&alg).UnmarshalText([]byte(algVal)); err != nil {
return NONE, err
}
return alg, nil
}
// PeekAlgorithmAndIssuer extracts the signing algorithm listed in the "alg"
// field and the issuer from the "iss" field of the serialized JWT buf's header
// and payload, attempting to unmarshal alg to Algorithm and iss to a string.
// An error will be returned if the Algorithm or Issuer fields are not
// specified in the JWT header and payload, or are otherwise invalid.
func PeekAlgorithmAndIssuer(buf []byte) (Algorithm, string, error) {
// get algorithm
alg, err := PeekAlgorithm(buf)
if err != nil {
return NONE, "", err
}
// get issuer
issuer, err := PeekPayloadField(buf, "iss")
if err != nil {
return NONE, "", err
}
return alg, issuer, nil
}