Files
autoglue/internal/auth/jwt_signer.go
2025-11-02 13:19:30 +00:00

139 lines
3.1 KiB
Go

package auth
import (
"crypto/ed25519"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"sync"
"time"
"github.com/glueops/autoglue/internal/keys"
"github.com/glueops/autoglue/internal/models"
"gorm.io/gorm"
)
type keyCache struct {
mu sync.RWMutex
pub map[string]interface{} // kid -> public key object
meta map[string]models.SigningKey
selKid string
selAlg string
selPriv any
}
var kc keyCache
// Refresh loads active keys into memory. Call on startup and periodically (ticker/cron).
func Refresh(db *gorm.DB, encKeyB64 string) error {
var rows []models.SigningKey
if err := db.Where("is_active = true AND (expires_at IS NULL OR expires_at > ?)", time.Now()).
Order("created_at desc").Find(&rows).Error; err != nil {
return err
}
pub := make(map[string]interface{}, len(rows))
meta := make(map[string]models.SigningKey, len(rows))
var selKid string
var selAlg string
var selPriv any
for i, r := range rows {
// parse public
block, _ := pem.Decode([]byte(r.PublicPEM))
if block == nil {
continue
}
var pubKey any
switch r.Alg {
case "RS256", "RS384", "RS512":
pubKey, _ = x509.ParsePKCS1PublicKey(block.Bytes)
if pubKey == nil {
// also allow PKIX format
if k, err := x509.ParsePKIXPublicKey(block.Bytes); err == nil {
pubKey = k
}
}
case "EdDSA":
k, err := x509.ParsePKIXPublicKey(block.Bytes)
if err == nil {
if edk, ok := k.(ed25519.PublicKey); ok {
pubKey = edk
}
}
}
if pubKey == nil {
continue
}
pub[r.Kid] = pubKey
meta[r.Kid] = r
// pick first row as current signer (most recent because of order desc)
if i == 0 {
privPEM := r.PrivatePEM
// decrypt if necessary
if len(privPEM) > 10 && privPEM[:10] == "enc:aesgcm" {
pt, err := keysDecrypt(encKeyB64, privPEM)
if err != nil {
continue
}
privPEM = string(pt)
}
blockPriv, _ := pem.Decode([]byte(privPEM))
if blockPriv == nil {
continue
}
switch r.Alg {
case "RS256", "RS384", "RS512":
if k, err := x509.ParsePKCS1PrivateKey(blockPriv.Bytes); err == nil {
selPriv = k
selAlg = r.Alg
selKid = r.Kid
} else if kAny, err := x509.ParsePKCS8PrivateKey(blockPriv.Bytes); err == nil {
if k, ok := kAny.(*rsa.PrivateKey); ok {
selPriv = k
selAlg = r.Alg
selKid = r.Kid
}
}
case "EdDSA":
if kAny, err := x509.ParsePKCS8PrivateKey(blockPriv.Bytes); err == nil {
if k, ok := kAny.(ed25519.PrivateKey); ok {
selPriv = k
selAlg = r.Alg
selKid = r.Kid
}
}
}
}
}
kc.mu.Lock()
defer kc.mu.Unlock()
kc.pub = pub
kc.meta = meta
kc.selKid = selKid
kc.selAlg = selAlg
kc.selPriv = selPriv
return nil
}
func keysDecrypt(encKey, enc string) ([]byte, error) {
return keysDecryptImpl(encKey, enc)
}
// indirection for same package
var keysDecryptImpl = func(encKey, enc string) ([]byte, error) {
return nil, errors.New("not wired")
}
// Wire up from keys package
func init() {
keysDecryptImpl = keysDecryptShim
}
func keysDecryptShim(encKey, enc string) ([]byte, error) {
return keys.Decrypt(encKey, enc)
}