122 lines
2.7 KiB
Go
122 lines
2.7 KiB
Go
package jwt
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/lestrrat-go/jwx/v3/jwa"
|
|
"github.com/lestrrat-go/jwx/v3/jwt"
|
|
)
|
|
|
|
var (
|
|
ErrTokenVerificationFailed = errors.New("token verification failed")
|
|
)
|
|
|
|
type tokenService struct {
|
|
secretKey []byte
|
|
accessTokenExpiration time.Duration
|
|
refreshTokenExpiration time.Duration
|
|
}
|
|
|
|
func New(secret string, ate, rfe time.Duration) TokenService {
|
|
secretKey := []byte(secret)
|
|
return &tokenService{
|
|
secretKey: secretKey,
|
|
accessTokenExpiration: ate,
|
|
refreshTokenExpiration: rfe,
|
|
}
|
|
}
|
|
|
|
func (ts tokenService) GenerateAccessRefreshTokenPair(
|
|
ctx context.Context,
|
|
tokenData *TokenData,
|
|
) (*AccessRefreshTokenPair, error) {
|
|
accessTokenExp := time.Now().Add(ts.accessTokenExpiration)
|
|
generateAccessJwt, err := ts.generateJwt(accessTokenExp, tokenData.Sub, "alinme-web")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
refreshTokenExp := time.Now().Add(ts.refreshTokenExpiration)
|
|
generateRefreshJwt, err := ts.generateJwt(refreshTokenExp, tokenData.Sub, "alinme-web")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &AccessRefreshTokenPair{
|
|
AccessToken: generateAccessJwt,
|
|
AccessTokenExpiresAt: accessTokenExp,
|
|
RefreshToken: generateRefreshJwt,
|
|
RefreshTokenExpiresAt: refreshTokenExp,
|
|
}, nil
|
|
}
|
|
|
|
func (ts tokenService) generateJwt(exp time.Time, sub string, aud string) (string, error) {
|
|
t, err := jwt.NewBuilder().
|
|
Subject(sub).
|
|
IssuedAt(time.Now()).
|
|
Issuer("alinme-server").
|
|
Audience([]string{aud}).
|
|
Expiration(exp).
|
|
Build()
|
|
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
signed, err := jwt.Sign(t, jwt.WithKey(jwa.HS256(), ts.secretKey))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(signed), nil
|
|
}
|
|
|
|
func (ts tokenService) VerifyToken(ctx context.Context, accessToken string) (*TokenPayload, error) {
|
|
parsed, err := jwt.Parse([]byte(accessToken), jwt.WithKey(jwa.HS256(), ts.secretKey))
|
|
if err != nil {
|
|
return nil, ErrTokenVerificationFailed
|
|
}
|
|
|
|
sub, ok := parsed.Subject()
|
|
if !ok {
|
|
return nil, ErrTokenVerificationFailed
|
|
}
|
|
|
|
aud, ok := parsed.Audience()
|
|
if !ok {
|
|
return nil, ErrTokenVerificationFailed
|
|
}
|
|
|
|
iat, ok := parsed.IssuedAt()
|
|
if !ok {
|
|
return nil, ErrTokenVerificationFailed
|
|
}
|
|
|
|
exp, ok := parsed.Expiration()
|
|
if !ok {
|
|
return nil, ErrTokenVerificationFailed
|
|
}
|
|
|
|
iss, ok := parsed.Issuer()
|
|
if !ok {
|
|
return nil, ErrTokenVerificationFailed
|
|
}
|
|
|
|
return &TokenPayload{
|
|
Sub: sub,
|
|
Aud: aud,
|
|
Iat: iat,
|
|
Exp: exp,
|
|
Iss: iss,
|
|
}, nil
|
|
}
|
|
|
|
func (ts tokenService) GenerateToken(ctx context.Context, input *GenerateTokenInput) (string, error) {
|
|
generateJwt, err := ts.generateJwt(time.Now().Add(time.Minute*5), input.Sub, input.Aud)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return generateJwt, nil
|
|
}
|