Passed
Push — main ( 3ec306...c5cfaa )
by Yume
01:53 queued 44s
created

jwt.GenerateToken   A

Complexity

Conditions 2

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 10
nop 2
dl 0
loc 16
rs 9.9
c 0
b 0
f 0

1 Method

Rating   Name   Duplication   Size   Complexity  
A jwt.NewJWTInstance 0 7 1
1
package jwt
2
3
import (
4
	"context"
5
	"strings"
6
	"time"
7
8
	"github.com/golang-jwt/jwt/v5"
9
	"github.com/memnix/memnix-rest/pkg/utils"
10
	"github.com/pkg/errors"
11
	"golang.org/x/crypto/ed25519"
12
)
13
14
type Instance struct {
15
	headerLen             int
16
	publicKey             ed25519.PublicKey
17
	privateKey            ed25519.PrivateKey
18
	signingMethod         jwt.SigningMethod
19
	ExpirationTimeInHours int
20
}
21
22
// NewJWTInstance return a new JwtInstance with the given parameters
23
func NewJWTInstance(headerLen, expirationTime int, publicKey ed25519.PublicKey, privateKey ed25519.PrivateKey) Instance {
24
	return Instance{
25
		headerLen:             headerLen,
26
		publicKey:             publicKey,
27
		privateKey:            privateKey,
28
		signingMethod:         jwt.SigningMethodEdDSA,
29
		ExpirationTimeInHours: expirationTime,
30
	}
31
}
32
33
// GenerateToken generates a jwt token from a user id
34
// and returns the token and an error
35
//
36
// It's signing method is defined in utils.JwtSigningMethod
37
// It's expiration time is defined in utils.GetExpirationTime
38
// It's secret key is defined in the environment variable SECRET_KEY
39
// see: utils/config.go for more information
40
func (instance Instance) GenerateToken(_ context.Context, userID uint) (string, error) {
41
	// Create the Claims for the token
42
	claims := jwt.NewWithClaims(instance.signingMethod, jwt.RegisteredClaims{
43
		Issuer:    utils.ConvertUIntToStr(userID),     // Issuer is the user id
44
		ExpiresAt: instance.CalculateExpirationTime(), // ExpiresAt is the expiration time
45
	})
46
47
	// Sign and get the complete encoded token as a string using the secret
48
	token, err := claims.SignedString(instance.privateKey)
49
	if err != nil {
50
		return "", errors.Wrap(err, "failed to sign")
51
	}
52
53
	return token, nil
54
}
55
56
// VerifyToken verifies a jwt token
57
// and returns the user id and an error
58
func (Instance) VerifyToken(token *jwt.Token) (uint, error) {
59
	// claims is of type jwt.MapClaims
60
	if claims, ok := token.Claims.(jwt.MapClaims); token.Valid && ok {
61
		// Get the issuer from the claims and convert it to uint
62
		userID, err := utils.ConvertStrToUInt(claims["iss"].(string))
63
		if err != nil {
64
			return 0, err
65
		}
66
67
		return userID, nil
68
	}
69
70
	return 0, errors.New("invalid token")
71
}
72
73
// GetToken gets a jwt.Token token from a string
74
// and returns the jwt.Token and an error
75
func (instance Instance) GetToken(_ context.Context, token string) (*jwt.Token, error) {
76
	// Parse takes the token string and a function for looking up the key.
77
	return jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
78
		if _, ok := token.Method.(*jwt.SigningMethodEd25519); !ok {
79
			return nil, errors.New("unexpected signing method")
80
		}
81
		return instance.publicKey, nil // Return the secret key as the signing key
82
	})
83
}
84
85
func (Instance) GetExpirationTime(token *jwt.Token) int64 {
86
	claims := token.Claims.(jwt.MapClaims)
87
	return int64(claims["exp"].(float64))
88
}
89
90
// extractToken function to extract token from header
91
func (instance Instance) extractToken(token string) string {
92
	// Normally Authorization HTTP header.
93
	onlyToken := strings.Split(token, " ") // Split token
94
	if len(onlyToken) == instance.headerLen {
95
		return onlyToken[1] // Return only token
96
	}
97
	return "" // Return empty string
98
}
99
100
// GetConnectedUserID gets the user id from a jwt token
101
func (instance Instance) GetConnectedUserID(ctx context.Context, tokenHeader string) (uint, error) {
102
	// Get the token from the Authorization header
103
	tokenString := instance.extractToken(tokenHeader)
104
105
	token, err := instance.GetToken(ctx, tokenString)
106
	if err != nil {
107
		return 0, err
108
	}
109
110
	// Check if the token is valid
111
	userID, err := instance.VerifyToken(token)
112
	if err != nil {
113
		return 0, err
114
	}
115
116
	return userID, nil
117
}
118
119
// CalculateExpirationTime returns the expiration time
120
func (instance Instance) CalculateExpirationTime() *jwt.NumericDate {
121
	return jwt.NewNumericDate(time.Now().Add(time.Hour * time.Duration(instance.ExpirationTimeInHours)))
122
}
123