all repos

onasty @ c5b3657

a one-time notes service

onasty/internal/jwtutil/jwtutil.go (view raw)

Smirnov Oleksandr Smirnov Oleksandr
ss2316544@gmail.com
refactor: fix annoyances (#97)..., 1 year ago
1
package jwtutil
2
3
import (
4
	"crypto/rand"
5
	"encoding/hex"
6
	"errors"
7
	"time"
8
9
	"github.com/golang-jwt/jwt/v5"
10
)
11
12
var ErrUnexpectedSigningMethod = errors.New("unexpected signing method")
13
14
type JWTTokenizer interface {
15
	// AccessToken generates a new access token with the given [Payload].
16
	AccessToken(pl Payload) (string, error)
17
18
	// RefreshToken generates a random string of 64 chars.
19
	RefreshToken() (string, error)
20
21
	// Parse parses the token and returns its [Payload].
22
	Parse(token string) (Payload, error)
23
}
24
25
// Payload the access token payload
26
type Payload struct {
27
	UserID string
28
}
29
30
var _ JWTTokenizer = (*JWTUtil)(nil)
31
32
type JWTUtil struct {
33
	signingKey     string
34
	accessTokenTTL time.Duration
35
}
36
37
func NewJWTUtil(signingKey string, accessTokenTTL time.Duration) *JWTUtil {
38
	return &JWTUtil{
39
		signingKey:     signingKey,
40
		accessTokenTTL: accessTokenTTL,
41
	}
42
}
43
44
func (j *JWTUtil) AccessToken(pl Payload) (string, error) {
45
	tok := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
46
		Subject:   pl.UserID,
47
		ExpiresAt: jwt.NewNumericDate(time.Now().Add(j.accessTokenTTL)),
48
	})
49
	return tok.SignedString([]byte(j.signingKey))
50
}
51
52
func (j *JWTUtil) RefreshToken() (string, error) {
53
	b := make([]byte, 32)
54
	if _, err := rand.Read(b); err != nil {
55
		return "", err
56
	}
57
	return hex.EncodeToString(b), nil
58
}
59
60
func (j *JWTUtil) Parse(token string) (Payload, error) {
61
	var claims jwt.RegisteredClaims
62
	_, err := jwt.ParseWithClaims(token, &claims, func(t *jwt.Token) (any, error) {
63
		if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
64
			return nil, ErrUnexpectedSigningMethod
65
		}
66
		return []byte(j.signingKey), nil
67
	})
68
	return Payload{
69
		UserID: claims.Subject,
70
	}, err
71
}