openova/core/services
e3mrah d681f64505
fix(catalyst-api): mint HS256 token on SME proxy calls (was forwarding incompatible RS256) (#1630)
PR #1625 shipped the /api/v1/sme/billing/vouchers/* proxies but the
SME gateway (core/services/gateway/proxy.go) rejects RS256 outright
— it only accepts HS256 signed with sme-secrets/JWT_SECRET. Result
on every fresh Sovereign: operator clicks on /bss/vouchers returned
silent 401 with no upstream audit trail.

This commit ships the bridge:

- core/services/shared/auth/mint_sme.go (new)
  - MintSMEAccessToken(secret, sub, email, role) → 5-min HS256 JWT
    in the wire shape billing's requireVoucherIssuer expects.
  - SMERoleFor(realmRoles, tier) → maps Keycloak roles + tier claim
    onto SME vocab (superadmin | sovereign-admin | member).
  - Pure, no IO, fully unit-tested (mint_sme_test.go).

- products/catalyst/bootstrap/api/internal/handler/sme_billing_vouchers.go
  - proxySMEVoucher now mints a fresh HS256 token per upstream hop
    from the operator's already-validated RS256 session claims and
    forwards that as Bearer to the SME gateway. RS256 header is no
    longer leaked upstream.
  - Unwired bridge (CATALYST_SME_JWT_SECRET empty) surfaces 503
    `sme-jwt-bridge-unwired` instead of the silent 401.

- products/catalyst/bootstrap/api/internal/handler/handler.go
  - h.smeJWTSecret field + SetSMEJWTSecret(secret) setter.

- products/catalyst/bootstrap/api/cmd/api/main.go
  - Reads CATALYST_SME_JWT_SECRET on startup and wires it.
  - Log line includes byte count only (never the secret value, per
    INVIOLABLE-PRINCIPLES.md #10).

- products/catalyst/chart/templates/api-deployment.yaml
  - New env CATALYST_SME_JWT_SECRET sourced from sme-secrets/JWT_SECRET
    in the same namespace (catalyst-system). optional: true so
    Sovereigns without marketplace surface a 503 rather than
    CreateContainerConfigError.

- products/catalyst/chart/templates/sme-services/sme-secrets.yaml
  - emberstack/reflector annotation block mirroring sme-secrets
    from `sme` ns into `catalyst-system` (Kubernetes secretKeyRef
    is same-namespace-only). Same pattern as cnpg-cluster.yaml
    and provisioning-github-token.yaml.

Operator-visible behaviour: the bridge is transparent on the happy
path (operator with sovereign-admin tier on a Sovereign with
marketplace enabled clicks /bss/vouchers → list returns). On the
unhappy paths the operator now sees a real status code:
  - 503 sme-jwt-bridge-unwired (chart wire missing) — actionable
  - 503 sme-gateway-unreachable (DNS NXDOMAIN) — pre-existing
  - 403 from billing's requireVoucherIssuer (role insufficient)
    — was silent 401 before, now propagates the real authz result.

Tests: core/services/shared/auth `go test ./...` PASS. catalyst-api
`go build ./...` PASS.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 10:11:04 +04:00
..
auth feat(sandbox+auth+newapi): Wave 1b — newapi proxy + BYOS + org-scoped JWT (#1619) 2026-05-18 08:43:11 +04:00
billing fix(billing): skip Stripe when voucher covers 100% of total (unblocks fully-paid voucher checkout) (#1628) 2026-05-18 09:44:22 +04:00
catalog fix(catalog): D27 — fresh-seed apps default Published+Deployable (#1584) 2026-05-17 09:28:35 +04:00
catalyst-catalog feat(catalog): catalog-svc HTTP REST service + chart wiring (slice L1+L2, #1097) (#1148) 2026-05-09 04:04:52 +04:00
domain fix(services): go mod tidy across sibling services after #798 shared deps bump (#821) 2026-05-04 22:35:46 +04:00
gateway fix(gateway): /redeem-preview + plans + addons must be public (D29) (#1559) 2026-05-16 23:17:04 +04:00
metering-sidecar feat(metering): NewAPI NATS publisher + sme-billing subscriber + POST /metering/record (#798) (#818) 2026-05-04 22:32:42 +04:00
notification feat(billing+notification): wire voucher-issued email (D28) (#1556) 2026-05-16 23:04:46 +04:00
provisioning fix(provisioning,catalog): parent-kustomization prefix collision + disable openclaw/stalwart-mail (#1043) 2026-05-06 10:21:39 +04:00
shared fix(catalyst-api): mint HS256 token on SME proxy calls (was forwarding incompatible RS256) (#1630) 2026-05-18 10:11:04 +04:00
tenant fix(sme): wire tenant + billing event dispatchers to NATS (was Redpanda-only, blocking convergence) (#1626) 2026-05-18 09:33:36 +04:00