spark-server/internal/module/admin/logic/jwt.go

84 lines
2.1 KiB
Go

package logic
import (
"errors"
"strings"
"time"
"github.com/golang-jwt/jwt/v5"
)
const TypeAdmin = "admin"
var (
ErrInvalidToken = errors.New("invalid admin token")
ErrWrongType = errors.New("token type mismatch")
)
// Claims matches Java JwtTokenProvider admin tokens (popadmin.wyrs365.com).
type Claims struct {
AdminID int64 `json:"adminId"`
Role string `json:"role"`
Type string `json:"type"`
Permissions string `json:"permissions"`
AllowedSites string `json:"allowedSites"`
SiteRealm string `json:"siteRealm"`
jwt.RegisteredClaims
}
// TokenService validates HS256 admin JWTs issued by the Java / popadmin login.
type TokenService struct {
secret []byte
}
func NewTokenService(secret string) *TokenService {
sec := []byte(strings.TrimSpace(secret))
if len(sec) < 32 {
padded := make([]byte, 32)
copy(padded, sec)
sec = padded
}
return &TokenService{secret: sec}
}
func (s *TokenService) Parse(tokenStr string) (*Claims, error) {
if s == nil || len(s.secret) == 0 {
return nil, ErrInvalidToken
}
t, err := jwt.ParseWithClaims(tokenStr, &Claims{}, func(t *jwt.Token) (interface{}, error) {
if t.Method != jwt.SigningMethodHS256 {
return nil, ErrInvalidToken
}
return s.secret, nil
})
if err != nil {
return nil, ErrInvalidToken
}
claims, ok := t.Claims.(*Claims)
if !ok || !t.Valid {
return nil, ErrInvalidToken
}
if claims.Type != "" && claims.Type != TypeAdmin {
return nil, ErrWrongType
}
return claims, nil
}
// CreateToken is for tests and local smoke only (production tokens come from Java login).
func (s *TokenService) CreateToken(adminID int64, username, role, permissions string) (string, error) {
now := time.Now()
claims := Claims{
AdminID: adminID,
Role: role,
Type: TypeAdmin,
Permissions: permissions,
RegisteredClaims: jwt.RegisteredClaims{
Subject: username,
IssuedAt: jwt.NewNumericDate(now),
ExpiresAt: jwt.NewNumericDate(now.Add(24 * time.Hour)),
},
}
tok := jwt.NewWithClaims(jwt.SigningMethodHS256, &claims)
return tok.SignedString(s.secret)
}