mirror of
https://github.com/hanzoai/gateway.git
synced 2026-10-30 15:04:44 +00:00
Minting is issuing authority. IAM signs the token, and the signature is where the authority is created. This edge VERIFIES a token and restates what it proved as headers, so a transport that cannot carry a token can read the same decision. Calling both operations "mint" put the issuer's job and the edge's under one word, and a word that covers two operations stops distinguishing them — a reader who thinks the edge mints reads it as a second issuer. So every use of `mint` for writing or stripping identity HEADERS becomes `write`, which is already the leaf's own word (hanzoai/authz: "strips every identity header on ingress and rewrites it from verified claims"). The identifiers follow: SetGatewayMinted/AssertGatewayMinted become SetGatewayWritten/AssertGatewayWritten, mintIdentity becomes writeIdentity, and waitlistMintedHeaders becomes waitlistHeaders — they are simply the guard's own response headers, and the adjective added nothing but the wrong verb. `mint` stays where it is correct: IAM issuing a token, a sign-in issuing a session cookie, commerce creating balance, and "the app the token was minted through". Two doc citations named symbols the migration removed and are re-pointed at the live ones: edge.Mint to edge.Render, gatewayMintedIdentityHeaders to authz.Headers. Co-authored-by: Hanzo Dev <[email protected]>
86 lines
2.8 KiB
Go
86 lines
2.8 KiB
Go
// Package middleware ships gateway-owned middleware for the zip web
|
|
// framework. JWT validation + identity-header stripping live here (and
|
|
// NOT in github.com/zap-proto/zip/middleware) because they are the
|
|
// gateway subsystem's responsibility per HIP-0106.
|
|
//
|
|
// Other subsystems mounted inside the unified cloud binary trust the
|
|
// gateway-written X-Org-Id header and do not re-validate JWTs
|
|
// themselves — re-running JWT validation per-subsystem is wasteful and
|
|
// risks divergent validation rules.
|
|
package middleware
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"github.com/zap-proto/zip"
|
|
|
|
gateway "github.com/hanzoai/gateway/v2"
|
|
)
|
|
|
|
// AuthVerifier is the interface Auth() consumes. The real implementation
|
|
// in hanzoai/iam or hanzoai/gateway-sdk satisfies it. A nil verifier on
|
|
// a request that has no gateway X-* headers and no Authorization bearer
|
|
// is rejected with 401.
|
|
type AuthVerifier interface {
|
|
// Verify validates the bearer token and returns the canonical
|
|
// X-* headers to write (Org / User / Email / IsAdmin / Roles).
|
|
Verify(ctx context.Context, bearer string) (Identity, error)
|
|
}
|
|
|
|
// Identity is the validated identity payload returned by AuthVerifier.
|
|
type Identity struct {
|
|
Org string
|
|
User string
|
|
UserEmail string
|
|
IsAdmin bool
|
|
Roles []string
|
|
}
|
|
|
|
// Auth validates incoming requests via verifier. When the request already
|
|
// carries gateway-written X-Org-Id (i.e. behind hanzoai/gateway), the
|
|
// verifier is bypassed and the headers are trusted. Otherwise the
|
|
// Authorization: Bearer <token> is verified.
|
|
//
|
|
// On successful gateway-trust or successful in-process verification,
|
|
// Auth marks the request as gateway-written via SetGatewayWritten so
|
|
// downstream subsystems can assert the trust boundary with
|
|
// gateway.AssertGatewayWritten(c).
|
|
//
|
|
// Pass a nil verifier to only accept gateway-written headers (no in-binary
|
|
// JWT validation).
|
|
func Auth(verifier AuthVerifier) zip.Handler {
|
|
return func(c *zip.Ctx) error {
|
|
// Trust the gateway path first.
|
|
if c.Org() != "" || c.User() != "" {
|
|
gateway.SetGatewayWritten(c)
|
|
return c.Continue()
|
|
}
|
|
if verifier == nil {
|
|
return zip.ErrUnauthorized("authentication required")
|
|
}
|
|
raw := c.Header("Authorization")
|
|
if !strings.HasPrefix(raw, "Bearer ") {
|
|
return zip.ErrUnauthorized("missing bearer token")
|
|
}
|
|
id, err := verifier.Verify(c.Context(), raw[len("Bearer "):])
|
|
if err != nil {
|
|
return zip.ErrUnauthorized("invalid token")
|
|
}
|
|
// Write the validated headers so handlers see the same shape as
|
|
// the gateway-fronted path.
|
|
req := c.Fiber().Request()
|
|
req.Header.Set("X-Org-Id", id.Org)
|
|
req.Header.Set("X-User-Id", id.User)
|
|
req.Header.Set("X-User-Email", id.UserEmail)
|
|
if id.IsAdmin {
|
|
req.Header.Set("X-User-IsAdmin", "true")
|
|
}
|
|
if len(id.Roles) > 0 {
|
|
req.Header.Set("X-Roles", strings.Join(id.Roles, ","))
|
|
}
|
|
gateway.SetGatewayWritten(c)
|
|
return c.Continue()
|
|
}
|
|
}
|