-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjwt.go
124 lines (115 loc) · 3.13 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
package web
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
"net/http"
"reflect"
"strings"
"time"
)
type JWT struct {
header string
Payload []byte
signature string
}
type JWTDefaultParams struct {
Iss string `json:"iss,omitempty"`
Exp int64 `json:"exp,omitempty"`
Sub string `json:"sub,omitempty"`
Aud string `json:"aud,omitempty"`
Nbf int64 `json:"nbf,omitempty"`
Iat int64 `json:"iat,omitempty"`
Jti string `json:"jti,omitempty"`
}
func encodeBase64(data string) string {
return base64.RawURLEncoding.EncodeToString([]byte(data))
}
func generateSignature(key []byte, data []byte) (string, error) {
hash := hmac.New(sha256.New, key)
_, err := hash.Write(data)
if err != nil {
return "", err
}
return encodeBase64(string(hash.Sum(nil))), nil
}
func CreateToken(key []byte, payloadData any) (string, error) {
header := `{"alg":"HS256","typ":"JWT"}`
payload, jsonErr := json.Marshal(payloadData)
if jsonErr != nil {
return "", fmt.Errorf("load JSON parsing error")
}
encodedHeader := encodeBase64(header)
encodedPayload := encodeBase64(string(payload))
HeaderAndPayload := encodedHeader + "." + encodedPayload
signature, err := generateSignature(key, []byte(HeaderAndPayload))
if err != nil {
return "", err
}
return HeaderAndPayload + "." + signature, nil
}
func parseJwt(token string, key []byte) (*JWT, error) {
jwtParts := strings.Split(token, ".")
if len(jwtParts) != 3 {
return nil, fmt.Errorf("illegal token")
}
encodedHeader := jwtParts[0]
encodedPayload := jwtParts[1]
signature := jwtParts[2]
confirmSignature, err := generateSignature(key, []byte(encodedHeader+"."+encodedPayload))
if err != nil {
return nil, fmt.Errorf("signature generation error")
}
if !hmac.Equal([]byte(signature), []byte(confirmSignature)) { //avoid timing attack
return nil, fmt.Errorf("token verification failed")
}
dstPayload, _ := base64.RawURLEncoding.DecodeString(encodedPayload)
return &JWT{encodedHeader, dstPayload, signature}, nil
}
// JwtConfirm is a middleware which defines to check the verification of jwt
func JwtConfirm(key []byte, headerKey string, obj any) Handler {
isMap := false
if reflect.TypeOf(obj).Kind() == reflect.Map {
isMap = true
}
hasExp := false
if isMap {
mp := obj.(map[string]any)
if _, has := mp["exp"]; has {
hasExp = true
}
} else {
metaObj := reflect.ValueOf(obj).Elem()
if metaObj.FieldByName("Exp") != (reflect.Value{}) {
hasExp = true
}
}
return HandlerFunc(func(c *Context) {
token := c.GetHeader(headerKey)
jwt, err := parseJwt(token, key)
if err != nil {
c.Fail(http.StatusUnauthorized, err.Error())
return
}
err = json.Unmarshal(jwt.Payload, &obj)
if err != nil {
c.Fail(http.StatusInternalServerError, err.Error())
return
}
if isMap {
if hasExp && int64(obj.(map[string]any)["exp"].(float64)) < time.Now().Unix() {
c.Fail(http.StatusUnauthorized, "session expiration")
return
}
} else {
if hasExp && reflect.ValueOf(obj).Elem().FieldByName("Exp").Int() < time.Now().Unix() {
c.Fail(http.StatusUnauthorized, "session expiration")
return
}
}
c.SetExtra("jwt", obj)
c.Next()
})
}