Documentation
¶
Overview ¶
Package authz provides a generic Kratos middleware for authorization. It is engine-agnostic: any Authorizer implementation can be injected.
Example usage:
import (
pkgauthz "github.com/Servora-Kit/servora/security/authz"
fgaengine "github.com/Servora-Kit/servora/security/authz/openfga"
)
mw = append(mw, pkgauthz.Server(
fgaengine.NewAuthorizer(fgaClient),
pkgauthz.WithRulesFunc(iamv1.AuthzRules),
))
Package authz provides a Kratos middleware for relationship-based authorization.
Engine model ¶
The Authorizer interface (Check / BatchCheck / ListAllowed) maps directly onto OpenFGA SDK and SpiceDB primitives. Both ReBAC backends support all three methods natively. The interface is not designed to host non-ReBAC engines (Cedar, Rego); those would require a separate abstraction.
Future: contextual tuples ¶
OpenFGA's "contextual tuples" (and SpiceDB's "caveats") express request-level facts that participate in a decision but are not persisted: device trust, active session, time-of-day, request region, etc.
When this is needed, the planned API is:
ctx = authz.WithContextualTuples(ctx, tuples...) // upstream mw injects authz.ContextualTuplesFromContext(ctx) []Tuple // engine adapter reads
The Authorizer interface signatures already accept context.Context as the first parameter, so no signature change will be required when this is added.
Audit integration ¶
Use authz.NewAuthzBridge(recorder) to forward every decision to obs/audit without per-call wiring.
Index ¶
- func MergeRules(ms ...map[string]AuthzRule) map[string]AuthzRule
- func NewAuthzBridge(recorder *audit.Recorder) func(context.Context, DecisionDetail)
- func Server(authorizer Authorizer, opts ...Option) middleware.Middleware
- type Authorizer
- type AuthzRule
- type CheckRequest
- type CheckResult
- type DecisionDetail
- type Option
- func WithCheckTimeout(d time.Duration) Option
- func WithDecisionLogger(fn func(ctx context.Context, detail DecisionDetail)) Option
- func WithDefaultObjectID(id string) Option
- func WithFailOpenOnMissingRule(alertFn func(ctx context.Context, operation string)) Option
- func WithRules(rules map[string]AuthzRule) Option
- func WithRulesFunc(fn func() map[string]AuthzRule) Option
- func WithRulesFuncs(fns ...func() map[string]AuthzRule) Option
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func MergeRules ¶
MergeRules merges multiple AuthzRule maps into one new map. Later maps take precedence on key conflicts (which should not occur in practice). Useful when a server registers services from multiple generated packages.
func NewAuthzBridge ¶ added in v0.4.0
func NewAuthzBridge(recorder *audit.Recorder) func(context.Context, DecisionDetail)
NewAuthzBridge returns a DecisionLogger that forwards every authorization decision to the audit Recorder as an AUTHZ_DECISION event.
Use it as a one-liner in middleware setup:
authz.Server(authorizer,
authz.WithRulesFunc(rules),
authz.WithDecisionLogger(authz.NewAuthzBridge(recorder)),
)
Closes TODO P0-2 (authz → audit auto-bridge).
If recorder is nil, the returned function is a safe no-op.
func Server ¶
func Server(authorizer Authorizer, opts ...Option) middleware.Middleware
Server returns a Kratos middleware that performs authorization checks.
Behavior:
- No transport in context → passthrough (non-server calls)
- No rule for operation → fail-closed (403 AUTHZ_NO_RULE)
- No rule for operation + WithFailOpenOnMissingRule set → alertFn invoked, handler called
- AUTHZ_MODE_NONE → skip (public endpoint)
- AUTHZ_MODE_CHECK, no actor or anonymous actor → 403 AUTHZ_DENIED
- AUTHZ_MODE_CHECK, nil authorizer → 503 AUTHZ_UNAVAILABLE
- AUTHZ_MODE_CHECK, allowed → handler called
- AUTHZ_MODE_CHECK, denied → 403 AUTHZ_DENIED
The OpenFGA principal is constructed as "<actor.Type()>:<actor.ID()>".
Types ¶
type Authorizer ¶
type Authorizer interface {
// Check returns whether subject has relation on objectType:objectID.
Check(ctx context.Context, subject, relation, objectType, objectID string) (allowed bool, err error)
// BatchCheck runs N checks in one round-trip; output order matches input.
// Implementations may internally chunk if the backend has per-call limits
// (OpenFGA caps at 50 per request).
BatchCheck(ctx context.Context, reqs []CheckRequest) ([]CheckResult, error)
// ListAllowed returns IDs of objects (of objectType) the subject has the
// given relation to. The returned strings are bare IDs without "type:" prefix.
// Useful for "list" endpoints — caller fetches by `WHERE id IN (...)`.
ListAllowed(ctx context.Context, subject, relation, objectType string) ([]string, error)
}
Authorizer is the interface for relationship-based authorization decisions. All three methods are required: implementations targeting non-ReBAC backends (e.g. pure Cedar/Rego) would need a different abstraction entirely, so we commit to the ReBAC shape rather than a sub-interface fan-out.
Method names match OpenFGA SDK semantics for direct mapping; SpiceDB (LookupResources / BulkCheck) maps cleanly as well.
type AuthzRule ¶
type AuthzRule struct {
Mode authzpb.AuthzMode
Relation string
ObjectType string
// IDField is the proto field name to extract object ID from the request.
// When empty, "default" is used as the object ID (singleton/platform-level checks).
IDField string
}
AuthzRule describes the authorization requirement for a single RPC operation.
type CheckRequest ¶ added in v0.4.0
CheckRequest is one item in a BatchCheck call.
type CheckResult ¶ added in v0.4.0
CheckResult is the per-item outcome of BatchCheck. Order matches the input []CheckRequest index.
type DecisionDetail ¶
type DecisionDetail struct {
Operation string
Subject string
Relation string
ObjectType string
ObjectID string
Allowed bool
Err error
}
DecisionDetail describes the result of a single authorization check. It is passed to the DecisionLogger callback after every check.
Cache-hit signals are intentionally absent — caching is an engine-internal optimization (see infra/openfga) and does not belong in audit semantics.
type Option ¶
type Option func(*serverConfig)
Option configures the Server middleware.
func WithCheckTimeout ¶ added in v0.4.0
WithCheckTimeout bounds the time spent in Authorizer.Check on each request. Zero (default) disables the deadline — the upstream context applies.
This protects business-RPC latency from a slow authorization backend (e.g. OpenFGA cross-region calls).
func WithDecisionLogger ¶
func WithDecisionLogger(fn func(ctx context.Context, detail DecisionDetail)) Option
WithDecisionLogger sets a callback invoked after every authorization check. Use this to bridge to audit.Recorder or any other audit sink. Replaces the old WithAuditRecorder; keeps pkg/authz free of pkg/audit dependency.
func WithDefaultObjectID ¶
WithDefaultObjectID overrides the fallback object ID used when IDField is empty. Defaults to "default".
func WithFailOpenOnMissingRule ¶ added in v0.4.0
WithFailOpenOnMissingRule changes the missing-rule policy from fail-closed (default — return AUTHZ_NO_RULE 403) to fail-open: the handler is called, and the alertFn callback is invoked so the gap is visible (oncall page, Slack, log warning, etc.).
Use during development or staged rollouts. NEVER use in production for security-sensitive services.
func WithRulesFunc ¶
WithRulesFunc sets the operation→rule mapping via a single function (e.g. generated AuthzRules()). The function is called once during middleware construction. To merge rules from multiple packages, prefer WithRulesFuncs.
func WithRulesFuncs ¶
WithRulesFuncs merges the rule maps returned by one or more generator functions (e.g. userpb.AuthzRules, authnpb.AuthzRules) into a single rule set. Later entries take precedence on key conflicts (which should not occur in practice). This is the preferred alternative to combining WithRules + MergeRules.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package noop provides a no-op Authorizer that always permits all requests.
|
Package noop provides a no-op Authorizer that always permits all requests. |
|
Package openfga provides an OpenFGA-based Authorizer implementation for pkg/authz.
|
Package openfga provides an OpenFGA-based Authorizer implementation for pkg/authz. |