-
Notifications
You must be signed in to change notification settings - Fork 0
/
generate.go
201 lines (181 loc) · 5.76 KB
/
generate.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
package jwt
import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"math/rand"
"strings"
"time"
"twiggg/packages/encryption"
)
type jwthead struct {
Typ string `json:"typ"` //JWT
Alg string `json:"alg"` // IMPORTANT : if alg=none, JWT needs to be declared unvalid
KeyId int `json:"kid"` // key int identifier of the randomly selected key from the keys list
}
type Jwtclaims struct {
Iss string `json:"iss"` //name of issuer
Iat int64 `json:"iat"` //issued at time
Exp int64 `json:"exp"` //expiration time
Nbf int64 `json:"nbf"` //cant be used before this date
Qsh string `json:"qsh"` //query string hash
Sub jwtcontext `json:"sub"` //subject = user
Aud []string `json:"aud"` //audience of the token
Jti string `json:"jti"` //jwt token unique identifier, in order to prevent replay attack. Used for one time use tokens.
}
type jwtcontext struct {
Username string `json:"username"` //alias of the user
//Userid string `json:"userid"` //user id used for shortcut access to db and user's perso space
Role string `json:"role"` //admin,test,standard,dev
AccessLevel string `json:"accesslevel"` //access level grants access to certain endpoints
LastConnection int64 `json:"lastconnection"` //time of last connection
//-> private means access to personal space + public pages. Can modify own info
//-> public means only access to public pages
//-> admin means access to everything and permissions to create read update delete
//-> analytics
//-> test
}
//encoding
//encodedString := base64.URLEncoding.EncodeToString([]byte(initialString))
//encodedString := base64.StdEncoding.EncodeToString([]byte(initialString))
//decoding
//decodedString, err := base64.URLEncoding.DecodeString(encodedString)
//decodedString, err := base64.StdEncoding.DecodeString(encodedString)
type KeyPicker interface {
pickKey() ([]byte, int, error)
valid() bool
keyValue(keyId int) ([]byte, error)
}
type KeySlice []string
var jwtkeys KeySlice = []string{"1K9l89853", "1a9s8n8", "l2a9h0s1s8e8n", "aeiouy"}
func (k KeySlice) pickKey() ([]byte, int, error) {
if len(k) > 0 {
n := rand.Intn(len(k))
key := k[n]
return []byte(key), n, nil
} else {
return nil, 0, errors.New("jwt/keySlice.pickKey : no keys stored in this slice")
}
}
func (k KeySlice) valid() bool {
if len(k) == 0 {
return false
} else {
for _, v := range k {
if len(v) == 0 {
return false
}
}
return true
}
}
func (k KeySlice) keyValue(keyId int) ([]byte, error) {
if len(k) < (keyId + 1) {
return []byte(""), errors.New("KeySlice.KeyValue: index out of range.")
} else {
return []byte(k[keyId]), nil
}
}
//-----------------------------------------------------------------------
func New(keypicker KeyPicker, algorithm string, issuer string, username string, role string, accesslevel string, audience []string, tokenId string) (string, error) {
//keypicker2 := new(KeyPicker)
//var keypicker2 KeyPicker
//fmt.Println("valid keypicker:", keypicker.valid())
if keypicker == nil || !keypicker.valid() {
//fmt.Println("jwt.Generate: keypicker was nil, changed to default in memory keyPicker")
//keypicker2 = jwtkeys
return "", errors.New("jwt.Generate: no valid KeyPicker provided")
} /*else {
keypicker2 = keypicker
}*/
// 1) head
//generate head
head := jwthead{}
head.Typ = "JWT"
switch strings.ToLower(algorithm) {
case "bcrypt":
head.Alg = "bcrypt"
case "hs256":
head.Alg = "hs256"
default:
head.Alg = "bcrypt"
}
//pick key
key, keyid, err := keypicker.pickKey()
if err != nil {
return "", errors.New("jwt.generate : could not pick a key")
}
head.KeyId = keyid
//fmt.Println(head)
//jsonmarshal head
json_h, err := json.Marshal(head)
//fmt.Println(json_h)
if err != nil {
//fmt.Println("error : ", err)
s := fmt.Sprint("jwt.generate : could not generate json head, %v", err.Error())
return "", errors.New(s)
}
//encode head
encodedHead := base64.URLEncoding.EncodeToString([]byte(json_h))
//fmt.Println(encodedHead)
//2) claims
//generate claims
claims := Jwtclaims{}
claims.Iss = issuer
claims.Iat = time.Now().Unix()
claims.Exp = time.Unix(claims.Iat, 0).Add(time.Second * 60 * 60 * 24 * 3).Unix() // 3days of validity
claims.Qsh = ""
context := jwtcontext{}
context.Username = username
context.Role = role
context.AccessLevel = accesslevel
claims.Sub = context
claims.Aud = audience
claims.Jti = tokenId
//json marshal claims
//fmt.Println(claims)
json_c, err := json.Marshal(claims)
//fmt.Println(json_c)
if err != nil {
//fmt.Println("error : ", err)
s := fmt.Sprint("jwt.generate : could not generate json claims, %v", err.Error())
return "", errors.New(s)
}
//encode claims
encodedClaims := base64.URLEncoding.EncodeToString([]byte(json_c))
//fmt.Println(encodedClaims)
//3) signature
//sign the token (encryption)
signingInput := fmt.Sprint(encodedHead, ".", encodedClaims)
signaturebytes := []byte{}
var err2 error = nil
switch head.Alg {
case "bcrypt":
//encryption.GenerateRandomBytes(n)
signaturebytes, err2 = encryption.Hashbcrypt(signingInput, key, 12)
if err != nil {
s := fmt.Sprint("jwt.generate : could not sign, %v", err2.Error())
return "", errors.New(s)
}
case "hs256":
signaturebytes, err2 = encryption.HashHS256(signingInput, key)
if err != nil {
s := fmt.Sprint("jwt.generate : could not sign, %v", err2.Error())
return "", errors.New(s)
}
default:
signaturebytes, err2 = encryption.Hashbcrypt(signingInput, key, 12)
if err != nil {
s := fmt.Sprint("jwt.generate : could not sign, %v", err2.Error())
return "", errors.New(s)
}
}
//encode signature
encodedSignature := base64.URLEncoding.EncodeToString(signaturebytes)
//4) token
jwtToken := fmt.Sprint(signingInput, ".", encodedSignature)
return jwtToken, nil
}
func Refresh() {
}