-
Notifications
You must be signed in to change notification settings - Fork 0
/
tokens.go
135 lines (117 loc) · 3.68 KB
/
tokens.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
package main
import (
"context"
"fmt"
"time"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
)
type RefreshToken struct {
ID primitive.ObjectID `bson:"_id,omitempty"`
UserID int `bson:"userID"`
Token string `bson:"token"`
Expiration time.Time `bson:"expiration"`
}
//check if a refresh token is already in the database
func DoesRefreshTokenAlreadyExists(token string, connection *mongo.Database) (bool, error) {
RefreshTokensCollection := connection.Collection("refreshTokens")
var result []RefreshToken
err := RefreshTokensCollection.
FindOne(context.TODO(), bson.M{"token": token}).
Decode(&result)
if err != nil {
if err == mongo.ErrNoDocuments {
return false, nil
}
return false, err
}
return true, nil
}
//generate a new token pair from the userID (matricola)
func GenerateTokenPair(userID int, connection *mongo.Database) (string, string, error) {
//generate the access token
jwtAccessToken, err := NewSignedToken(NewCustomClaims(userID))
if err != nil {
return "", "", err
}
token := RefreshToken{}
token.UserID = userID
//generate a random string for the refresh token
//and set the expiration time to 1 week
for {
token.Token = generateRandomString(64)
token.Expiration = time.Now().Add(time.Hour * 24 * 7)
//check if the access token is already in the database
found, err := DoesRefreshTokenAlreadyExists(token.Token, connection)
if err != nil {
return "", "", err
}
if !found {
break
}
}
_, err = connection.Collection("refreshTokens").InsertOne(context.TODO(), token)
if err != nil {
return "", "", err
}
return jwtAccessToken, token.Token, nil
}
//check if the refresh token is expired
func IsRefreshTokenExpired(token string, connection *mongo.Database) (bool, error) {
RefreshTokensCollection := connection.Collection("refreshTokens")
var result RefreshToken
err := RefreshTokensCollection.
FindOne(context.TODO(), bson.M{"token": token}).
Decode(&result)
if err != nil {
if err == mongo.ErrNoDocuments {
return true, nil
}
return true, err
}
//check if the expiration time is "before" the current time
return result.Expiration.Before(time.Now()), nil
}
//get the student struct from the access token (can't use the refresh token)
func GetUserFromAccessToken(accessToken string, connection *mongo.Database) (Student, error) {
claims, err := ParseToken(accessToken)
if err != nil {
return Student{}, err
}
var user Student
err = connection.Collection("users").
FindOne(context.TODO(), bson.M{"userID": claims.UserID}).
Decode(&user)
return user, err
}
//generate a new token pair given a valid refresh token
//the refresh token allows us to get the userID that will be used to generat a new token pair
func GenerateNewTokenPairFromRefreshToken(refreshToken string, connection *mongo.Database) (string, string, error) {
//check if the refresh token is expired
isExpired, err := IsRefreshTokenExpired(refreshToken, connection)
if err != nil {
return "", "", err
}
if isExpired {
return "", "", fmt.Errorf("refresh token is expired")
}
//get the user id from the refresh token
var token RefreshToken
tokensCollection := connection.Collection("refreshTokens")
err = tokensCollection.
FindOne(context.TODO(), bson.M{"token": refreshToken}).
Decode(&token)
if err != nil {
if err == mongo.ErrNoDocuments {
return "", "", fmt.Errorf("no refresh token found")
}
return "", "", err
}
_, err = tokensCollection.DeleteOne(context.TODO(), bson.M{"token": refreshToken})
if err != nil {
return "", "", err
}
//generate the new token pair
return GenerateTokenPair(token.UserID, connection)
}