/
jwt_auth.go
129 lines (105 loc) · 3.1 KB
/
jwt_auth.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
package utils
import (
"context"
"fmt"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/oryx-systems/makao/pkg/makao/application/common"
"github.com/oryx-systems/makao/pkg/makao/application/common/helpers"
)
// Create the JWT key used to create the signature
var (
jwtKey = []byte(helpers.MustGetEnvVar("JWT_SECRET"))
)
// TokenResponse represents the response from the token endpoint
type TokenResponse struct {
Token string `json:"token"`
ExpiresIn time.Time `json:"expiresIn"`
}
// Create a struct that will be encoded to a JWT.
// We add jwt.RegisteredClaims as an embedded type, to provide fields like expiry time
type Claims struct {
UserID string `json:"user_id"`
jwt.RegisteredClaims
}
// GenerateJWTToken generates a JWT token
func GenerateJWTToken(userID string) (*TokenResponse, error) {
if userID == "" {
return nil, fmt.Errorf("user id is required")
}
// Create the Claims
claims := &Claims{
UserID: userID,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * 3)),
Issuer: issuer,
IssuedAt: jwt.NewNumericDate(time.Now()),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString(jwtKey)
if err != nil {
return nil, err
}
return &TokenResponse{
Token: tokenString,
ExpiresIn: claims.ExpiresAt.Time,
}, nil
}
// ValidateJWTToken validates a JWT token
func ValidateJWTToken(tokenString string) (*TokenResponse, error) {
tkn, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
return jwtKey, nil
})
if err != nil {
return nil, err
}
claims, ok := tkn.Claims.(*Claims)
if !ok {
return nil, err
}
if !tkn.Valid {
return nil, err
}
// We ensure that a new token is not issued until enough time has elapsed
// In this case, a new token will only be issued if the old token is within
// 30 seconds of expiry. Otherwise, return a bad request status
if time.Until(claims.ExpiresAt.Time) > 30*time.Second {
// Now, create a new token for the current use, with a renewed expiration time
claims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Minute * 5))
claims.IssuedAt = jwt.NewNumericDate(time.Now())
// Declare the token with the algorithm used for signing, and the claims
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
// Create the JWT string
tokenString, err = token.SignedString(jwtKey)
if err != nil {
return nil, err
}
return &TokenResponse{
Token: tokenString,
ExpiresIn: claims.ExpiresAt.Time,
}, nil
}
return &TokenResponse{
Token: tokenString,
ExpiresIn: claims.ExpiresAt.Time,
}, nil
}
// GetLoggedInUser retrieves the logged in user from the context
func GetLoggedInUser(ctx context.Context) (string, error) {
UID := ctx.Value(common.AuthTokenContextKey).(string)
tkn, err := jwt.ParseWithClaims(UID, &Claims{}, func(token *jwt.Token) (interface{}, error) {
return jwtKey, nil
})
if err != nil {
return "", err
}
claims, ok := tkn.Claims.(*Claims)
if !ok {
return "", err
}
if !tkn.Valid {
return "", err
}
return claims.UserID, nil
}