all repos

onasty @ a9ae72ba6d9d01fcc52c5cd4577e93e3aefb8602

a one-time notes service

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

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
chore: fix doc comments, 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 new refresh token
19
	RefreshToken() (string, error)
20
21
	// Parse parses the token and returns the payload
22
	Parse(token string) (Payload, error)
23
}
24
25
type Payload struct {
26
	UserID string
27
}
28
29
var _ JWTTokenizer = (*JWTUtil)(nil)
30
31
type JWTUtil struct {
32
	signingKey     string
33
	accessTokenTTL time.Duration
34
}
35
36
func NewJWTUtil(signingKey string, accessTokenTTL time.Duration) *JWTUtil {
37
	return &JWTUtil{
38
		signingKey:     signingKey,
39
		accessTokenTTL: accessTokenTTL,
40
	}
41
}
42
43
func (j *JWTUtil) AccessToken(pl Payload) (string, error) {
44
	tok := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
45
		Subject:   pl.UserID,
46
		ExpiresAt: jwt.NewNumericDate(time.Now().Add(j.accessTokenTTL)),
47
	})
48
	return tok.SignedString([]byte(j.signingKey))
49
}
50
51
func (j *JWTUtil) RefreshToken() (string, error) {
52
	b := make([]byte, 32)
53
	if _, err := rand.Read(b); err != nil {
54
		return "", err
55
	}
56
	return hex.EncodeToString(b), nil
57
}
58
59
func (j *JWTUtil) Parse(token string) (Payload, error) {
60
	var claims jwt.RegisteredClaims
61
	_, err := jwt.ParseWithClaims(token, &claims, func(t *jwt.Token) (any, error) {
62
		if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
63
			return nil, ErrUnexpectedSigningMethod
64
		}
65
		return []byte(j.signingKey), nil
66
	})
67
	return Payload{
68
		UserID: claims.Subject,
69
	}, err
70
}