/
token.go
132 lines (122 loc) · 3.31 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
package auth
import (
"context"
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"errors"
"fmt"
"strings"
"time"
"github.com/no-src/gofs/auth"
"github.com/no-src/nsgo/jsonutil"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
// Token an authentication and token component
type Token interface {
// GenerateToken generate a new token by user info
GenerateToken(in *LoginUser) (token string, err error)
// IsLogin resolve the token in the context.Context
IsLogin(ctx context.Context) (user *auth.User, err error)
}
type token struct {
users []*auth.User
timeoutSeconds int64
tokenExpiresSeconds int64
secret string
iv string
}
// NewToken create a default implementation of the Token
func NewToken(users []*auth.User, secret string) (Token, error) {
err := checkTokenSecret(secret)
if err != nil {
return nil, err
}
return &token{
users: users,
timeoutSeconds: 60,
tokenExpiresSeconds: 60 * 30,
secret: secret,
iv: "nosrc-gofs-token",
}, nil
}
func (t *token) GenerateToken(in *LoginUser) (token string, err error) {
var user *auth.User
for _, u := range t.users {
if u.UserName() == in.GetUsername() && u.Password() == in.GetPassword() && in.GetTimestamp()+t.timeoutSeconds > time.Now().Unix() {
user = u
}
}
if user != nil {
return t.encodeToken(user)
}
return token, errors.New("login failed")
}
func (t *token) IsLogin(ctx context.Context) (user *auth.User, err error) {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, status.Errorf(codes.Unauthenticated, "login failed")
}
var authorization string
mdv := md.Get("authorization")
if len(mdv) > 0 {
authorization = strings.TrimPrefix(mdv[0], "Bearer ")
}
tu, err := t.decodeToken(authorization)
if err != nil {
return nil, err
}
for _, u := range t.users {
if u.UserName() == tu.GetUsername() && tu.Expires > time.Now().Unix() {
user = u
}
}
return user, nil
}
func (t *token) encodeToken(u *auth.User) (token string, err error) {
tu := TokenUser{
UserId: int32(u.UserId()),
Username: u.UserName(),
Expires: time.Now().Unix() + t.tokenExpiresSeconds,
}
data, err := jsonutil.Marshal(tu)
if err != nil {
return token, err
}
block, err := aes.NewCipher([]byte(t.secret))
if err != nil {
return token, err
}
stream := cipher.NewCFBEncrypter(block, []byte(t.iv))
dst := make([]byte, len(data))
stream.XORKeyStream(dst, data)
token = base64.StdEncoding.EncodeToString(dst)
return token, nil
}
func (t *token) decodeToken(token string) (tu *TokenUser, err error) {
if len(token) == 0 {
return nil, errors.New("token can't be empty")
}
data, err := base64.StdEncoding.DecodeString(token)
if err != nil {
return nil, err
}
block, err := aes.NewCipher([]byte(t.secret))
if err != nil {
return nil, err
}
stream := cipher.NewCFBDecrypter(block, []byte(t.iv))
dst := make([]byte, len(data))
stream.XORKeyStream(dst, data)
err = jsonutil.Unmarshal(dst, &tu)
return tu, err
}
func checkTokenSecret(secret string) error {
length := len(secret)
if length == 16 || length == 24 || length == 32 {
return nil
}
return fmt.Errorf("invalid token secret size => %d, current must be either 16, 24, or 32 bytes, please check the -token_secret flag", length)
}