forked from Laisky/go-utils
-
Notifications
You must be signed in to change notification settings - Fork 0
/
jwt.go
240 lines (202 loc) · 5.9 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
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
235
236
237
238
239
240
package utils
import (
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/pkg/errors"
)
var (
// SignMethodHS256 use HS256 for jwt
SignMethodHS256 = jwt.SigningMethodHS256
// SignMethodES256 use ES256 for jwt
SignMethodES256 = jwt.SigningMethodES256
defaultSignMethod = SignMethodHS256
)
// ParseJWTTokenWithoutValidate parse and get payload without validate jwt token
func ParseJWTTokenWithoutValidate(token string) (payload jwt.MapClaims, err error) {
var jt *jwt.Token
if jt, err = jwt.Parse(token, func(_ *jwt.Token) (interface{}, error) {
return "", nil
}); jt == nil && err != nil {
return nil, errors.Wrap(err, "parse jwt token")
}
var ok bool
if payload, ok = jt.Claims.(jwt.MapClaims); !ok {
return nil, errors.New("payload type not match `map[string]interface{}`")
}
return payload, nil
}
// JWT is token utils that support HS256/ES256
type JWT struct {
secret,
priKey, pubKey []byte
signingMethod jwt.SigningMethod
}
// JWTOptFunc options to setup JWT
type JWTOptFunc func(*JWT) error
// WithJWTSignMethod set jwt signing method
func WithJWTSignMethod(method jwt.SigningMethod) JWTOptFunc {
return func(e *JWT) error {
e.signingMethod = method
return nil
}
}
// WithJWTSecretByte set jwt symmetric signning key
func WithJWTSecretByte(secret []byte) JWTOptFunc {
return func(e *JWT) error {
e.secret = secret
return nil
}
}
// WithJWTPriKeyByte set jwt asymmetrical private key
func WithJWTPriKeyByte(prikey []byte) JWTOptFunc {
return func(e *JWT) error {
e.priKey = prikey
return nil
}
}
// WithJWTPubKeyByte set jwt asymmetrical public key
func WithJWTPubKeyByte(pubkey []byte) JWTOptFunc {
return func(e *JWT) error {
e.pubKey = pubkey
return nil
}
}
type jwtDivideOpt struct {
priKey, pubKey,
secret []byte
}
// JWTDiviceOptFunc options to use separate secret for every user in parsing/signing
type JWTDiviceOptFunc func(*jwtDivideOpt) error
// WithJWTDivideSecret set symmetric key for each signning/verify
func WithJWTDivideSecret(secret []byte) JWTDiviceOptFunc {
return func(opt *jwtDivideOpt) error {
opt.secret = secret
return nil
}
}
// WithJWTDividePriKey set asymmetrical private key for each signning/verify
func WithJWTDividePriKey(priKey []byte) JWTDiviceOptFunc {
return func(opt *jwtDivideOpt) error {
opt.priKey = priKey
return nil
}
}
// WithJWTDividePubKey set asymmetrical public key for each signning/verify
func WithJWTDividePubKey(pubKey []byte) JWTDiviceOptFunc {
return func(opt *jwtDivideOpt) error {
opt.pubKey = pubKey
return nil
}
}
// NewJWT create new JWT utils
func NewJWT(opts ...JWTOptFunc) (e *JWT, err error) {
e = &JWT{
signingMethod: defaultSignMethod,
}
for _, optf := range opts {
if err = optf(e); err != nil {
return nil, errors.Wrap(err, "apply option")
}
}
return
}
// Sign sign claims to token
func (e *JWT) Sign(claims jwt.Claims, opts ...JWTDiviceOptFunc) (string, error) {
switch e.signingMethod {
case SignMethodHS256:
return e.SignByHS256(claims, opts...)
case SignMethodES256:
return e.SignByES256(claims, opts...)
}
return "", fmt.Errorf("unknown signmethod `%s`", e.signingMethod)
}
// SignByHS256 signing claims by HS256
func (e *JWT) SignByHS256(claims jwt.Claims, opts ...JWTDiviceOptFunc) (string, error) {
opt := &jwtDivideOpt{
secret: e.secret,
}
for _, optf := range opts {
if err := optf(opt); err != nil {
return "", errors.Wrap(err, "apply optf")
}
}
token := jwt.NewWithClaims(SignMethodHS256, claims)
return token.SignedString(opt.secret)
}
// SignByES256 signing claims by ES256
func (e *JWT) SignByES256(claims jwt.Claims, opts ...JWTDiviceOptFunc) (string, error) {
opt := &jwtDivideOpt{
pubKey: e.pubKey,
priKey: e.priKey,
}
for _, optf := range opts {
if err := optf(opt); err != nil {
return "", errors.Wrap(err, "apply optf")
}
}
token := jwt.NewWithClaims(SignMethodES256, claims)
priKey, err := jwt.ParseECPrivateKeyFromPEM(opt.priKey)
if err != nil {
return "", errors.Wrap(err, "parse private key")
}
return token.SignedString(priKey)
}
// ParseClaims parse token to claims
func (e *JWT) ParseClaims(token string, claimsPtr jwt.Claims, opts ...JWTDiviceOptFunc) error {
if !IsPtr(claimsPtr) {
return errors.New("claimsPtr must be a pointer")
}
switch e.signingMethod {
case SignMethodHS256:
return e.ParseClaimsByHS256(token, claimsPtr, opts...)
case SignMethodES256:
return e.ParseClaimsByES256(token, claimsPtr, opts...)
default:
return fmt.Errorf("unknown sign method `%s`", e.signingMethod)
}
}
// ParseClaimsByHS256 parse token to claims by HS256
func (e *JWT) ParseClaimsByHS256(token string, claimsPtr jwt.Claims, opts ...JWTDiviceOptFunc) error {
opt := &jwtDivideOpt{
secret: e.secret,
}
for _, optf := range opts {
if err := optf(opt); err != nil {
return errors.Wrap(err, "apply optf")
}
}
if _, err := jwt.ParseWithClaims(token, claimsPtr, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return opt.secret, nil
}); err != nil {
return errors.Wrap(err, "parse token by hs256")
}
return nil
}
// ParseClaimsByES256 parse token to claims by ES256
func (e *JWT) ParseClaimsByES256(token string, claimsPtr jwt.Claims, opts ...JWTDiviceOptFunc) error {
opt := &jwtDivideOpt{
pubKey: e.pubKey,
priKey: e.priKey,
}
for _, optf := range opts {
if err := optf(opt); err != nil {
return errors.Wrap(err, "apply optf")
}
}
pubKey, err := jwt.ParseECPublicKeyFromPEM(opt.pubKey)
if err != nil {
return errors.Wrap(err, "parse es256 public key")
}
if _, err = jwt.ParseWithClaims(token, claimsPtr, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodECDSA); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return pubKey, nil
}); err != nil {
return errors.Wrap(err, "parse token by es256")
}
return nil
}