-
Notifications
You must be signed in to change notification settings - Fork 0
/
token.go
109 lines (89 loc) · 2.57 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
package token
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"errors"
"strings"
"time"
)
var b64 = base64.URLEncoding
type header struct {
Typ string `json:"typ"`
Alg string `json:"alg"`
}
// Claims is the type of the token's body. It is basically an alias of map to
// be more readable and easy to use.
type Claims map[string]interface{}
// SignHS256 encode a set of claims in a HS256 encoded token.
func SignHS256(claims Claims, secret []byte) (string, error) {
rawHeader, _ := json.Marshal(header{
Typ: "JWT",
Alg: "HS256",
}) // Skipping the error because the error isn't input-dependant and thus
// can't happen during execution.
encodedHeader := b64.EncodeToString(rawHeader)
rawClaims, err := json.Marshal(claims)
if err != nil {
return "", err
}
encodedClaims := b64.EncodeToString(rawClaims)
h := hmac.New(sha256.New, secret)
h.Write([]byte(encodedHeader + "." + encodedClaims))
rawSignature := h.Sum(nil)
encodedSignature := b64.EncodeToString(rawSignature)
return encodedHeader + "." + encodedClaims + "." + encodedSignature, nil
}
// ParseHS256 parse and validate a HS256-encoded token. For now, it doesn't
// check anything else than the signature.
func ParseHS256(token string, secret []byte) (Claims, error) {
chunks := strings.Split(token, ".")
if len(chunks) != 3 {
return nil, errors.New("malformed token")
}
decodedHeader, err := b64.DecodeString(chunks[0])
if err != nil {
return nil, err
}
var header header
err = json.Unmarshal(decodedHeader, &header)
if err != nil {
return nil, err
}
if header.Typ != "JWT" {
return nil, errors.New("invalid token type")
}
if header.Alg != "HS256" {
return nil, errors.New("invalid token algorithm")
}
h := hmac.New(sha256.New, secret)
h.Write([]byte(chunks[0] + "." + chunks[1]))
rawSignature := h.Sum(nil)
decodedSignature, err := b64.DecodeString(chunks[2])
if err != nil {
return nil, err
}
if !hmac.Equal(rawSignature, decodedSignature) {
return nil, errors.New("invalid signature")
}
var claims Claims
decodedClaims, err := b64.DecodeString(chunks[1])
if err != nil {
return nil, err
}
err = json.Unmarshal(decodedClaims, &claims)
if err != nil {
return nil, err
}
if exp, found := claims["exp"]; found {
exp, ok := exp.(float64)
if !ok {
return nil, errors.New("exp claim must be a NumericalDate")
}
if time.Unix(int64(exp), 0).Before(time.Now()) {
return nil, errors.New("expired token")
}
}
return claims, nil
}