mirror of
https://github.com/hanzoai/commerce.git
synced 2026-10-30 15:08:42 +00:00
Multi-tenant commerce and billing: orders, subscriptions, invoices, plans, usage metering and a credit ledger. One Go binary. MIT OR Apache-2.0. Claude-Session: https://claude.ai/code/session_01SAtaDFm8VDiWrhYHNNtxMw
51 lines
2.0 KiB
Go
51 lines
2.0 KiB
Go
package store
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"github.com/zap-proto/zip"
|
|
|
|
"github.com/hanzoai/commerce/middleware"
|
|
"github.com/hanzoai/commerce/util/json/http"
|
|
"github.com/hanzoai/commerce/util/permission"
|
|
)
|
|
|
|
// mintStorefrontToken mints (idempotently) the org's storefront read key — an
|
|
// access token carrying ONLY permission.Published, the least-privilege key a
|
|
// logged-out shopper's storefront uses to read the org's published catalog
|
|
// (design path b: "a per-org storefront Published key, minted once, kept in
|
|
// KMS"). It is org-bound (signed by the org's own SecretKey, subject = org id),
|
|
// so unlike the shared service token it can NEVER act on another tenant and
|
|
// carries no write/admin scope.
|
|
//
|
|
// Admin-gated (the route wraps middleware.TokenRequired(permission.Admin)): only
|
|
// an org admin — or the platform service token scoped to this org via X-Org-Id —
|
|
// may mint its own org's storefront key. Re-minting rotates the key (RemoveToken
|
|
// drops the prior "storefront" token, invalidating it).
|
|
//
|
|
// Returns { status, org, token }. The caller stores `token` in KMS and injects
|
|
// it as HANZO_COMMERCE_STOREFRONT_TOKEN on the storefront deployment.
|
|
func mintStorefrontToken(c *zip.Ctx) error {
|
|
// Authoritative admin gate. The route's TokenRequired(Admin) NO-OPS on the
|
|
// IAM path (EdgeAuth/gateway-minted identity), so enforce admin here the same
|
|
// way the money handlers do — IAM-aware, fail-closed — or a non-admin org
|
|
// member could mint their org's storefront key. RequireAdmin renders the
|
|
// rejection itself, so returning nil here ends the chain.
|
|
if !middleware.RequireAdmin(c) {
|
|
return nil
|
|
}
|
|
|
|
org, ok := middleware.GetOrganizationOK(c)
|
|
if !ok || org == nil {
|
|
return http.Fail(c, 400, "organization required", errors.New("no organization in context"))
|
|
}
|
|
|
|
org.RemoveToken("storefront")
|
|
token := org.AddToken("storefront", permission.Published)
|
|
if err := org.Put(); err != nil {
|
|
return http.Fail(c, 500, "failed to persist storefront token", err)
|
|
}
|
|
|
|
return http.Render(c, 200, map[string]any{"status": "ok", "org": org.Name, "token": token})
|
|
}
|