-
Notifications
You must be signed in to change notification settings - Fork 0
/
token.go
167 lines (143 loc) · 3.44 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
package token
import (
"errors"
"fmt"
"net/http"
"net/url"
"strings"
"github.com/dgrijalva/jwt-go"
)
var (
// ErrEmptyToken empty token
ErrEmptyToken = errors.New("token: empty token")
)
// M payload in jwt
type M map[string]interface{}
var conf *Config
// Config jwt config
type Config struct {
Secret string
// LookupMethod methods to lookup token
// Here are support 3 methods:
// ["query-<param>", "header-<key>", "cookie-<key>"]
// eg: ["query-token", "header-token", "cookie-token"]
// Will start looking up the token from the beginning of the array, and stop until it is found
LookupMethod []string
lookupMethods [][]string
}
// Init -
func (c *Config) Init() error {
c.lookupMethods = nil
for _, method := range c.LookupMethod {
method = strings.TrimSpace(method)
if method == "" {
continue
}
parts := strings.SplitN(method, "-", 2)
if len(parts) < 2 {
continue
}
k := strings.TrimSpace(parts[0])
v := strings.TrimSpace(parts[1])
c.lookupMethods = append(c.lookupMethods, []string{k, v})
}
return nil
}
// Init init config
func Init(c *Config) error {
if c == nil {
return errors.New("nil config")
}
conf = c
return conf.Init()
}
// ParseFromRequest parse token from request
func ParseFromRequest(r *http.Request) (payload M, err error) {
token, err := getToken(r)
if err != nil {
return
}
return Parse(token, conf.Secret)
}
func getToken(r *http.Request) (string, error) {
var token string
var err error
for _, method := range conf.lookupMethods {
if token != "" {
break
}
k := method[0]
v := method[1]
switch k {
case "header":
token, err = jwtFromHeader(r, v)
case "query":
token, err = jwtFromQuery(r, v)
case "cookie":
token, err = jwtFromCookie(r, v)
}
}
if err != nil {
return "", err
}
return token, nil
}
func jwtFromHeader(r *http.Request, key string) (string, error) {
auth := r.Header.Get(key)
if auth == "" {
return "", ErrEmptyToken
}
var t string
// Parse the header to get the token part.
fmt.Sscanf(auth, "Bearer %s", &t)
if t == "" {
return "", ErrEmptyToken
}
return t, nil
}
func jwtFromQuery(r *http.Request, key string) (string, error) {
if values, ok := r.URL.Query()[key]; ok && len(values) > 0 {
return values[0], nil
}
return "", ErrEmptyToken
}
func jwtFromCookie(r *http.Request, name string) (string, error) {
cookie, err := r.Cookie(name)
if err != nil {
return "", err
}
token, _ := url.QueryUnescape(cookie.Value)
if token == "" {
return "", ErrEmptyToken
}
return token, nil
}
// Parse validates the token with the specialized secret,
func Parse(tokenString string, secret string) (payload M, err error) {
// Parse the token.
token, err := jwt.Parse(tokenString, secretFunc(secret))
if err != nil {
return
}
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
return M(claims), nil
}
return
}
// secretFunc validates the secret format.
func secretFunc(secret string) jwt.Keyfunc {
return func(token *jwt.Token) (interface{}, error) {
// Make sure the `alg` is what we except.
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, jwt.ErrSignatureInvalid
}
return []byte(secret), nil
}
}
// GenerateToken generate a token with given payload
func GenerateToken(payload M) (string, error) {
claims := jwt.MapClaims(payload)
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// sign the token with the specified secret.
return token.SignedString([]byte(conf.Secret))
}