Skip to main content

Authorization and Entitlements

This guide explains the architectural patterns governing how users are authorized to perform actions and how feature sets are enabled across SaaS environments.

Overview

Framework M separates identity from access control through a Port/Adapter (Hexagonal) architecture. This ensures that the core business logic remains agnostic to whether you are using a simple local database check, a complex Open Policy Agent (ABAC) implementation, or a graph-based SpiceDB (ReBAC) system.


1. The Three Layers of Access

Access control is resolved through three distinct layers, each governed by its own Protocol.

LayerResponsibilityProtocolDefault Implementation
Authentication"Who is this user?"IdentityProtocolCitadel IAM / Local JWT
Entitlement"What SaaS features does this tenant pay for?"FeatureEntitlementProtocolSubscription Check
Authorization"What can this user do with this resource?"PermissionProtocolDocType RBAC

2. Stateless Policy Evaluation

The PermissionProtocol is built around a stateless request/response cycle. Instead of the permission engine fetching data from databases (which creates tight coupling), the framework provides all necessary context upfront.

The PolicyEvaluateRequest

This request contains four critical context blocks:

  1. Principal: The identity performing the action (User ID).
  2. Action: The intent (e.g., read, write, submit).
  3. Resource: The entity being accessed (e.g., Invoice).
  4. Attributes: Dynamic metadata (e.g., User's Department, Document Status, Tenant ID).

This stateless design allows the "Adapter" (like OPA) to make a sub-millisecond decision without secondary I/O lookups.


3. SaaS Entitlements (The Guard Rail)

Entitlements sit "above" permissions. They govern the visibility of entire feature sets before user-level roles are even checked.

Architectural Flow:

  1. Navigation Resolution: The NavigationPolicyEngine asks the entitlement system: "Is wms_ai_packing enabled for this tenant?"
  2. Pruning: If the tenant hasn't paid for the AI Pack, the entire navigation branch is pruned at the source.
  3. Zero-Visibility: This prevents users from seeing "Access Denied" screens for features their company hasn't purchased, providing a cleaner UI experience.

4. Port/Adapter Extensibility

Because these are defined as Protocols in framework-m-core, you can swap the engine as your application scales:

Level 1: Standard RBAC (Local)

For small to medium apps, permissions are defined in DocType.Meta. The RbacPermissionAdapter reads these rules directly from the code.

Level 2: OPA/ABAC (Decoupled)

As enterprise rules become complex (e.g., "Allow write if user is in HQ and time is 9-5"), you can swap the adapter to OpaPermissionAdapter. The rules are now managed in Rego files outside the Python codebase.

Level 3: SpiceDB/ReBAC (Relational)

For complex organizational hierarchies, you can swap to SpiceDbPermissionAdapter. It evaluates permissions by traversing relationship graphs (e.g., "User is a manager of the owner of this invoice").


5. Usage in Navigation

The Navigation Architecture uses these protocols to resolve what the user sees in the Rail & Drawer:

Navigation Policy

This multi-stage resolution ensures that the navigation manifest delivered to the browser (RFC-0010) is already pre-filtered for both SaaS plan and User roles.