-
Notifications
You must be signed in to change notification settings - Fork 0
/
token.go
135 lines (125 loc) · 4.2 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
133
134
135
package main
import (
"fmt"
"log"
"net/http"
"os"
"strconv"
"strings"
"time"
"github.com/dgrijalva/jwt-go"
"github.com/twinj/uuid"
)
func GetNewToken(userid uint64) (*TokenDetails, error) {
var err error
td.AccessToken, err = CreateAccessToken(userid)
fmt.Printf("access token is : %v", td.AccessToken)
if err != nil {
return nil, err
}
td.RefreshToken, err = CreateRefreshToken(userid)
if err != nil {
return nil, err
}
return td, err
}
/*
CreateAccessToken : Generating JWT Token which is valid for 15 minutes
Some Implementation Loopholes :
1. user can log in, then decide to log out immediately, but the user’s JWT remains valid until the expiration time is reached.
2. The JWT might be hijacked and used by a hacker without the user doing anything about it until the token expires.
3. The user will need to re-login after the token expires, thereby leading to a poor user experience.
*/
func CreateAccessToken(userid uint64) (string, error) {
td.AtExpires = time.Now().Add(time.Minute * 15).Unix()
td.AccessUuid = uuid.NewV4().String()
var err error
//Creating Access Token
err = os.Setenv("ACCESS_SECRET", "jdnfksdmfksd") //this should be in an env file
if err != nil {
log.Fatal(err)
}
atClaims := jwt.MapClaims{}
atClaims["authorized"] = true
atClaims["access_uuid"] = td.AccessUuid
atClaims["user_id"] = userid
atClaims["exp"] = td.AtExpires
at := jwt.NewWithClaims(jwt.SigningMethodHS256, atClaims)
td.AccessToken, err = at.SignedString([]byte(os.Getenv("ACCESS_SECRET")))
if err != nil {
log.Fatal(err)
}
fmt.Printf("access token is : %v", td.AccessToken)
return td.AccessToken, nil
}
/*
Since the uuid is unique each time it is created, a user can create more than one token. This happens when a user is logged in on different devices.
The user can also log out from any of the devices without them being logged out from all devices. How cool!
*/
// CheckValidToken : Check the validity of this token, whether it is still useful or it has expired
func CheckValidToken(r *http.Request) error {
token, err := VerifyTokenFromHeaders(r)
if err != nil {
return err
}
if _, ok := token.Claims.(jwt.Claims); !ok && !token.Valid {
return err
}
return nil
}
/*
For a production grade application, it is highly recommended to store JWTs in an HttpOnly cookie.
To achieve this, while sending the cookie generated from the backend to the frontend (client),
a HttpOnly flag is sent along with the cookie, instructing the browser not to display the cookie through the client-side scripts.
Doing this can prevent XSS (Cross Site Scripting) attacks.
JWT can also be stored in browser local storage or session storage.
Storing a JWT this way can expose it to several attacks such as XSS mentioned above, so it is generally less secure when compared to using `HttpOnly cookie technique.
*/
// GetTokenFromHeaders : Extract the token from Headers
func GetTokenFromHeaders(r *http.Request) string {
bearerToken := r.Header.Get("Authorization")
//normally Authorization the_token_xxx
strArr := strings.Split(bearerToken, " ")
if len(strArr) == 2 {
return strArr[1]
}
return ""
}
// VerifyTokenFromHeaders : Verify the headers token with SigningMethodHMAC
func VerifyTokenFromHeaders(r *http.Request) (*jwt.Token, error) {
tokenString := GetTokenFromHeaders(r)
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
//Make sure that the token method conform to "SigningMethodHMAC"
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte(os.Getenv("ACCESS_SECRET")), nil
})
if err != nil {
return nil, err
}
return token, nil
}
// GetTokenMetadata : Get JWT Token Metadata
func GetTokenMetadata(r *http.Request) (*TokenMetadata, error) {
token, err := VerifyTokenFromHeaders(r)
if err != nil {
return nil, err
}
claims, ok := token.Claims.(jwt.MapClaims)
if ok && token.Valid {
accessUuid, ok := claims["access_uuid"].(string)
if !ok {
return nil, err
}
userId, err := strconv.ParseUint(fmt.Sprintf("%.f", claims["user_id"]), 10, 64)
if err != nil {
return nil, err
}
return &TokenMetadata{
AccessUuid: accessUuid,
UserId: userId,
}, nil
}
return nil, err
}