dbots/internal/token/token.go

67 lines
1.6 KiB
Go

package token
import (
"encoding/hex"
"errors"
"time"
"aidanwoods.dev/go-paseto"
)
const TokenDuration = 7 * 24 * time.Hour
const (
claimUserID = "uid"
)
// KeyFromHex loads a V4 symmetric key from a 64-char hex string (AUTH_PASETO_KEY).
func KeyFromHex(h string) (paseto.V4SymmetricKey, error) {
b, err := hex.DecodeString(h)
if err != nil {
return paseto.V4SymmetricKey{}, err
}
return paseto.V4SymmetricKeyFromBytes(b)
}
// IssueToken creates a PASETO v4 local (encrypted) token carrying the
// user ID and a jti that maps to a row in the sessions table.
func IssueToken(key paseto.V4SymmetricKey, userID, jti string) (string, error) {
tok := paseto.NewToken()
tok.SetIssuedAt(time.Now())
tok.SetNotBefore(time.Now())
tok.SetExpiration(time.Now().Add(TokenDuration))
tok.SetJti(jti)
if err := tok.Set(claimUserID, userID); err != nil {
return "", err
}
return tok.V4Encrypt(key, nil), nil
}
// Claims holds the verified payload extracted from a token.
type Claims struct {
UserID string
JTI string
}
// VerifyToken decrypts and validates a PASETO v4 local token.
func VerifyToken(key paseto.V4SymmetricKey, raw string) (Claims, error) {
parser := paseto.NewParser()
parser.AddRule(paseto.NotExpired())
parser.AddRule(paseto.ValidAt(time.Now()))
tok, err := parser.ParseV4Local(key, raw, nil)
if err != nil {
return Claims{}, err
}
var userID string
if err := tok.Get(claimUserID, &userID); err != nil {
return Claims{}, errors.New("missing uid claim")
}
jti, err := tok.GetJti()
if err != nil {
return Claims{}, errors.New("missing jti claim")
}
return Claims{UserID: userID, JTI: jti}, nil
}