package moduletrust import ( "encoding/base64" "encoding/json" "fmt" "net/http" "time" "github.com/golang-jwt/jwt/v5" ) type Claims struct { Subject string `json:"sub"` TenantSlug string `json:"tenant"` jwt.RegisteredClaims } // Issue signiert ein Token mit dem aktuellen Signierschluessel und traegt // dessen KID im JWT-Header ein — der Verifier auf Modulseite waehlt darueber // den passenden oeffentlichen Schluessel aus PublicKeySet() aus. func (m *KeyManager) Issue(subject, tenantSlug string, ttl time.Duration) (string, error) { key, err := m.SigningKey() if err != nil { return "", err } now := time.Now() claims := Claims{ Subject: subject, TenantSlug: tenantSlug, RegisteredClaims: jwt.RegisteredClaims{ IssuedAt: jwt.NewNumericDate(now), ExpiresAt: jwt.NewNumericDate(now.Add(ttl)), }, } token := jwt.NewWithClaims(jwt.SigningMethodEdDSA, claims) token.Header["kid"] = key.KID return token.SignedString(key.Private) } type jwksResponse struct { Keys []jwksKey `json:"keys"` } type jwksKey struct { Kid string `json:"kid"` PublicKey string `json:"public_key"` // base64 (raw Ed25519, 32 Byte) } // ServeJWKS liefert alle bekannten oeffentlichen Schluessel als JSON — // Module fragen dies periodisch ab (nicht pro Request), siehe Verifier. func (m *KeyManager) ServeJWKS(w http.ResponseWriter, r *http.Request) { set := m.PublicKeySet() resp := jwksResponse{Keys: make([]jwksKey, 0, len(set))} for kid, pub := range set { resp.Keys = append(resp.Keys, jwksKey{Kid: kid, PublicKey: base64.StdEncoding.EncodeToString(pub)}) } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(resp) } // ParseJWKS dekodiert die JSON-Antwort von ServeJWKS zurueck in kid->PublicKey // — Hilfsfunktion fuer Module, die JWKS per HTTP abrufen. func ParseJWKS(data []byte) (map[string][]byte, error) { var resp jwksResponse if err := json.Unmarshal(data, &resp); err != nil { return nil, fmt.Errorf("jwks parsen: %w", err) } out := make(map[string][]byte, len(resp.Keys)) for _, k := range resp.Keys { raw, err := base64.StdEncoding.DecodeString(k.PublicKey) if err != nil { return nil, fmt.Errorf("oeffentlichen schluessel %q dekodieren: %w", k.Kid, err) } out[k.Kid] = raw } return out, nil }