BOC v1.0: RS256 auth, Ledger integration, Prometheus metrics, CI/CD, backup

This commit is contained in:
Bernt
2026-07-28 23:08:32 +00:00
parent 0b4f160af1
commit af874040ca
11541 changed files with 1654104 additions and 1103 deletions
+1
View File
@@ -7,3 +7,4 @@ RprzZGLlOpwCslfvNFrz6vB9HnUxYHIPexB54YwTtUZjpoz+Um/A5y6nAn94P/E5
RqTqVp80vHPpTXL/KSOwU6E8NQYHWPhp1eziiq0hfTOZeDzZIeDKn+tHNwBiU71q
KQIDAQAB
-----END PUBLIC KEY-----
+71 -2
View File
@@ -3,13 +3,18 @@ package auth
import (
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"fmt"
"math/big"
"net/http"
"os"
"strings"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/rs/zerolog/log"
)
// RS256Service validates RS256 JWT tokens using a public key
@@ -20,6 +25,66 @@ type RS256Service struct {
audience string
}
// JWKS represents a JSON Web Key Set
type JWKS struct {
Keys []JWK `json:"keys"`
}
// JWK represents a JSON Web Key
type JWK struct {
Kty string `json:"kty"`
N string `json:"n"`
E string `json:"e"`
Use string `json:"use"`
Alg string `json:"alg"`
Kid string `json:"kid"`
}
// NewRS256ServiceFromURL fetches JWKS from URL and creates RS256Service
func NewRS256ServiceFromURL(jwksURL string) (*RS256Service, error) {
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Get(jwksURL)
if err != nil {
return nil, fmt.Errorf("failed to fetch JWKS: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("JWKS endpoint returned %d", resp.StatusCode)
}
var jwks JWKS
if err := json.NewDecoder(resp.Body).Decode(&jwks); err != nil {
return nil, fmt.Errorf("failed to decode JWKS: %w", err)
}
if len(jwks.Keys) == 0 {
return nil, fmt.Errorf("no keys in JWKS")
}
// Use first signing key
key := jwks.Keys[0]
nBytes, err := base64.RawURLEncoding.DecodeString(key.N)
if err != nil {
return nil, fmt.Errorf("failed to decode N: %w", err)
}
eBytes, err := base64.RawURLEncoding.DecodeString(key.E)
if err != nil {
return nil, fmt.Errorf("failed to decode E: %w", err)
}
pub := &rsa.PublicKey{
N: new(big.Int).SetBytes(nBytes),
E: int(new(big.Int).SetBytes(eBytes).Int64()),
}
return &RS256Service{
publicKey: pub,
issuer: "prexo-identity",
audience: "prexo",
}, nil
}
// NewRS256Service loads the public key from a PEM file
func NewRS256Service(publicKeyPath string) (*RS256Service, error) {
pemData, err := os.ReadFile(publicKeyPath)
@@ -71,6 +136,7 @@ func (s *RS256Service) Middleware() func(http.Handler) http.Handler {
tokenString := strings.TrimPrefix(authHeader, "Bearer ")
claims, err := s.ValidateToken(tokenString)
if err != nil {
log.Warn().Err(err).Msg("token validation failed")
http.Error(w, `{"error":"invalid token"}`, http.StatusUnauthorized)
return
}
@@ -89,8 +155,11 @@ func (s *RS256Service) ValidateToken(tokenString string) (*Claims, error) {
}
return s.publicKey, nil
})
if err != nil || !token.Valid {
return nil, fmt.Errorf("invalid token: %w", err)
if err != nil {
return nil, fmt.Errorf("token parse error: %w", err)
}
if !token.Valid {
return nil, fmt.Errorf("token invalid")
}
mapClaims, ok := token.Claims.(jwt.MapClaims)