Skip to main content

authorization-patterns

Guides expert-level authorization patterns implementation: security and design-patterns decision frameworks, production-ready patterns, and concrete templates for authorization patterns workflows. Use when the user asks about authorization patterns, authorization patterns configuration, or security best practices for authorization projects. Do NOT use when the user needs a different backend infrastructure capability -- check sibling skills in the backend infrastructure subcategory.

Datos de origen

Repositorio
FerroxLabs/murage
Última actividad en el origen
7 de octubre de 2026 a las 18:05
Idioma detectado de SKILL.md
inglés
Estrellas
10
Forks
2

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Explorador de archivos
2 archivos

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name
authorization-patterns
description
Guides expert-level authorization patterns implementation: security and design-patterns decision frameworks, production-ready patterns, and concrete templates for authorization patterns workflows. Use when the user asks about authorization patterns, authorization patterns configuration, or security best practices for authorization projects. Do NOT use when the user needs a different backend infrastructure capability -- check sibling skills in the backend infrastructure subcategory.
license
Apache-2.0
metadata
{"author":"Ferrox Labs","version":"1.0.0","tags":"security backend design-patterns","category":"backend-systems","subcategory":"backend-infrastructure","depends":"","disclaimer":"none","difficulty":"intermediate"}
# Authorization Patterns ## When to Use **Use this skill when:** - User asks how to implement access control in a backend service and needs to choose between RBAC, ABAC, ReBAC, or PBAC models - User needs to design a permission system for a multi-tenant SaaS application with varying customer-level access policies - User wants to implement fine-grained authorization and evaluate tools like OPA (Open Policy Agent), Casbin, or a homegrown policy engine - User is building a microservices architecture and needs to decide whether to enforce authorization at the gateway, at each service, or in a centralized policy decision point (PDP) - User's current authorization code has grown into an unmaintainable tangle of inline if-checks scattered across business logic and needs to be refactored into a coherent pattern - User needs to implement row-level security, field-level visibility control, or resource-scoped permissions in an API - User asks about JWT claims design, token scope strategies, or how to embed authorization data in tokens vs. look it up at runtime - User is adding audit logging to an authorization system and needs to capture the right decision metadata **Do NOT use this skill when:** - User asks about authentication (login flows, session management, OAuth2/OIDC provider configuration) -- use the authentication skill instead - User needs API gateway configuration in isolation without an authorization design question attached -- use the API gateway skill - User asks about database access control at the PostgreSQL row security policy level as a standalone DBA topic -- use the database security skill - User needs general JWT library usage without an authorization design decision -- use the token management skill - User is asking about network-layer security (firewall rules, VPC policies, IAM for cloud infrastructure) -- use the cloud IAM or infrastructure security skill - User wants input validation or injection prevention -- use the input validation skill - User only needs rate limiting or throttling without an access control component -- use the rate limiting skill --- ## Process ### 1. Identify the Authorization Model Required Start by gathering the access control requirements precisely. The model you choose has deep structural implications and is very hard to change later. - **RBAC (Role-Based Access Control):** Permissions are assigned to roles, and users are assigned to roles. Correct for most SaaS applications with a limited set of distinct user types (admin, editor, viewer). Works when the number of roles is stable and fewer than ~20. - **ABAC (Attribute-Based Access Control):** Permissions are evaluated from a policy that considers attributes of the subject (user), resource, action, and environment. Required when "a manager can edit documents owned by members of their department" type logic appears. ABAC policies are written in XACML, Rego (OPA), or Cedar. - **ReBAC (Relationship-Based Access Control):** Permissions derive from the graph of relationships between users and resources. Google Zanzibar is the canonical example. Required when "a user can edit a document if they are a member of a group that has editor access to the folder containing that document" -- deeply nested, recursive relationship traversal. - **PBAC (Policy-Based Access Control):** A superset of ABAC where policies are centrally authored, versioned, and evaluated. Common in regulated enterprises using OPA or AWS Cedar. - Ask the user: How many distinct permission types exist? Do permissions depend on resource ownership? Do they depend on organizational hierarchy? Do they change per tenant? Quantitative thresholds: - Fewer than 10 roles, no attribute conditions: pure RBAC is correct - 10-50 roles with some attribute conditions: RBAC with attribute augmentation - Dynamic policies that reference resource properties or relationships: ABAC or ReBAC - Multi-tenant where each tenant defines their own policies: PBAC with tenant-scoped policy namespacing ### 2. Design the Permission Model Once the model is chosen, define its constituent parts explicitly before writing any code. - Define the **principal** -- who is making the request. Can be a user, a service account, an API key holder, or a machine identity. Each has different token formats and trust levels. - Define the **resource** -- what is being accessed. Use a consistent resource naming scheme: `organizations/{org_id}/projects/{project_id}/documents/{doc_id}`. This hierarchical naming directly maps to how ReBAC and ABAC policies traverse ownership. - Define the **action** -- what operation is being performed. Stick to a canonical verb set: `create`, `read`, `update`, `delete`, `list`, `publish`, `archive`. Avoid inventing ad-hoc verbs per endpoint. - Define **permission strings** as `resource_type:action` -- for example `document:read`, `project:admin`, `organization:billing_manage`. This is the unit that gets assigned to roles or evaluated in policies. - Document the full permission matrix as a table before writing code. Rows are resource types, columns are actions, cells describe which roles or attributes grant access. - Decide whether permissions are **additive** (a user's effective permissions are the union of all their roles' permissions) or **deny-override** (explicit deny beats any allow). Almost all modern systems use additive; deny-override is reserved for regulatory contexts like healthcare or financial auditing. ### 3. Choose the Enforcement Architecture Where and how authorization checks run has major performance and correctness implications. - **Inline enforcement (embedded policy library):** The application imports a library (Casbin, OPA Go SDK, or a homegrown role checker) and evaluates authorization directly in the request handler. Latency is <1ms. Correct for monoliths or single-service APIs. Risk: policy logic gets duplicated across services over time. - **Sidecar enforcement:** A policy engine (OPA) runs as a sidecar container. The application sends authorization requests over localhost HTTP/gRPC. Latency is 1-5ms. Correct for containerized microservices. Policies are centrally authored and pushed to all sidecars via a bundle server. - **Centralized PDP (Policy Decision Point):** A standalone authorization service (Open Policy Agent, Oso, SpiceDB, Permit.io's engine) evaluates all policy decisions. Applications act as PEPs (Policy Enforcement Points) and call the PDP over the network. Latency is 5-50ms per decision without caching. Correct for organizations that need a single audit trail and unified policy management. Requires a caching layer (Redis with short TTL, or the PDP's built-in cache) to avoid the network being a bottleneck. - **Gateway enforcement:** Authorization is checked at the API gateway or service mesh layer (Envoy + OPA, Kong + a plugin, AWS API Gateway authorizers). Coarse-grained decisions ("is this token valid and does it have the `api:access` scope") belong here. Fine-grained decisions ("can this user read *this specific document*") must remain in the service, because the gateway does not have resource-level context. - **Never** rely solely on gateway enforcement. Always enforce at the service layer as well (defense in depth). The gateway catches unauthenticated requests; the service enforces business-level permissions. - For microservices, the canonical pattern is: JWT validation at gateway → scope check at gateway → resource-level authorization check inside the service (using its own PEP call to the PDP). ### 4. Design Token Claims and Context Propagation Authorization decisions depend on context. Decide what lives in the token vs. what is fetched at decision time. - **In the token (JWT claims):** User ID (`sub`), tenant ID (`tid`), a small stable set of roles (`roles: ["editor", "billing_admin"]`), token expiry (`exp`), and the issuer (`iss`). Keep JWT payload under 4KB. Do not embed resource-specific permissions in the token -- they become stale the moment permissions change. - **Fetched at decision time:** Current group memberships, resource ownership, dynamic attributes, recent policy changes. These are fetched by the PDP or by the service's authorization middleware from a fast read store (a read replica or a dedicated permission cache in Redis). - For multi-tenant systems, always include the `tid` (tenant ID) claim and validate it against the resource being accessed. A valid JWT with `tid: acme_corp` must never grant access to resources owned by `tenant_id: globex`. - Propagate authorization context through internal service calls via a dedicated header (`X-Authorization-Context`) that carries the original user identity, not just the calling service's identity. Services should evaluate permissions as if the original user is making the call, not as if a trusted backend is making the call. - Use a short token TTL for high-security contexts (15 minutes for access tokens) and rely on refresh tokens for session continuity. Permission changes take effect at the next token refresh, so TTL is your maximum permission propagation delay. ### 5. Implement the Policy Enforcement Points (PEPs) The PEP is the code that halts request processing and asks "is this allowed?" Write PEPs as middleware, not inline business logic. - Create a single `authorize(principal, resource, action)` function or middleware that is the only place authorization is evaluated. Business logic functions never contain `if user.role == "admin"` checks -- they call `authorize()`. - In Express/Node.js: write an `authz` middleware factory that takes resource type and action, reads the resource identifier from `req.params`, calls the authorization service, and either calls `next()` or returns 403. - In Go: use a middleware function that wraps handlers. Pass the resolved principal through `context.Context` so downstream handlers can access it without re-evaluating auth. - In Python/FastAPI: use a `Depends()` injection chain where `get_current_user()` returns the principal and `authorize_resource(action)` is a dependency that performs the check. - Always return 403 (Forbidden) when authorization fails, never 404, unless the resource existence itself is sensitive and must be hidden (in which case 404 is intentional and must be documented). - Log every authorization decision: principal ID, resource ID, action, decision (allow/deny), policy version, and timestamp. This is the audit log. Write it to an append-only store. ### 6. Implement the Policy Decision Logic Write the actual policy rules using the chosen model's primitives. **For RBAC with a permission table (SQL example):** ```sql -- Three-table RBAC schema CREATE TABLE roles (id UUID PRIMARY KEY, name TEXT UNIQUE, tenant_id UUID); CREATE TABLE role_permissions (role_id UUID, permission TEXT, PRIMARY KEY (role_id, permission)); CREATE TABLE user_roles (user_id UUID, role_id UUID, tenant_id UUID, PRIMARY KEY (user_id, role_id)); -- Authorization query: does user X have permission Y on tenant Z? SELECT EXISTS ( SELECT 1 FROM user_roles ur JOIN role_permissions rp ON ur.role_id = rp.role_id WHERE ur.user_id = $1 AND ur.tenant_id = $2 AND rp.permission = $3 ); ``` **For ABAC with OPA/Rego:** ```rego package authz default allow = false allow { input.action == "document:read" user_is_member_of_org[input.resource.org_id] not document_is_archived } user_is_member_of_org[org_id] { data.memberships[input.principal.id][org_id] } document_is_archived { data.documents[input.resource.id].status == "archived" } ``` **For ReBAC with SpiceDB (Zanzibar-inspired):** Define a schema that expresses relations: ``` definition user {} definition organization { relation member: user relation admin: user permission manage = admin } definition document { relation owner: user relation org: organization permission read = owner + org->member permission write = owner + org->admin } ``` Then call `CheckPermission(user:alice, read, document:123)` which traverses the relationship graph. - Cache policy decisions aggressively when the policy and data are stable. Use a read-through cache keyed by `(principal_id, resource_id, action)` with a TTL of 30-60 seconds. Invalidate on role changes and resource ownership changes. - Version your policies. Tag each policy artifact with a semantic version. Log the policy version alongside every authorization decision so you can replay decisions during audit investigations. ### 7. Handle Authorization in List and Search Endpoints The hardest part of authorization is not single-resource checks -- it is filtering collections efficiently. - Naive approach: fetch all resources, then filter in application code. This is correct but breaks at scale (fetching 100,000 rows to return 12 is not acceptable). - Correct approach for SQL: push authorization filters directly into the database query using a join or a subquery against the permissions table. This is **query rewriting**. - For PostgreSQL, use Row Level Security (RLS) policies as a defense-in-depth layer in addition to application-level authorization, not as a replacement for it. - For Elasticsearch or similar search engines, use document-level security by filtering on a `permitted_users` or `permitted_roles` field stored alongside each document. - For ReBAC systems like SpiceDB, use the `LookupResources` API which returns all resource IDs a user has a given permission on. Use these IDs as a filter clause in your database query. - Set a maximum result set size for any list operation (typically 1000 items). Paginate using cursor-based pagination. Never allow unbounded queries. - Document that list endpoints return "what the caller is allowed to see" not "all resources that exist." This is a security contract, not a convenience feature. ### 8. Test and Audit the Authorization System Authorization bugs are often silent -- the system returns 200 OK when it should return 403. - Write a **permission matrix test suite** that explicitly tests every role-action-resource combination listed in the design document. For N roles and M permissions, write N×M tests. These are not unit tests -- they are behavioral contracts. - Test **negative cases** explicitly: a user with `document:read` cannot perform `document:delete`. A user in tenant A cannot access resources in tenant B. A deactivated user's token cannot perform any action. - Test **privilege escalation paths**: can a user with `user:invite` grant themselves `admin` role? Can a user modify their own JWT claims? (They cannot -- but verify this in your token validation tests.) - Use **mutation testing** (tools like Stryker or mutmut) on authorization logic specifically. If deleting a condition in a policy does not cause a test to fail, you have a missing test. - For OPA/Rego policies, use `opa test` with a dedicated test file that covers every rule branch. - Run a quarterly **permission audit**: export the full user-role-permission mapping and verify it against the intended design. Look for permission creep (users accumulating roles over time that they no longer need). - Set up alerting on authorization failure rates. A spike in 403s often indicates an automated attack (permission enumeration), a deployment that broke auth config, or a client bug worth investigating. --- ## Output Format When responding to a user authorization design question, produce the following structured output: ``` ## Authorization Design -- [System Name] ### Model Selection | Criterion | Assessment | |----------------------------------|---------------------------------| | Number of roles | [count] | | Attribute conditions required | [yes/no -- describe] | | Relationship graph traversal | [yes/no -- describe depth] | | Multi-tenant isolation | [yes/no] | | Policy ownership (dev vs. ops) | [who authors policies] | | **Recommended Model** | **[RBAC / ABAC / ReBAC / PBAC]**| ### Permission Matrix | Resource Type | create | read | update | delete | list | admin | |-------------------|--------|------|--------|--------|------|-------| | [resource_type_1] | [roles]| [roles]| [roles]| [roles]| [roles]| [roles]| | [resource_type_2] | ... | ... | ... | ... | ... | ... | ### Enforcement Architecture - **Gateway layer:** [scope/token validation only -- yes/no, what checks] - **Service layer:** [resource-level PEP -- middleware name/location] - **PDP:** [inline / sidecar / centralized -- tool name] - **Cache strategy:** [TTL, cache key, invalidation trigger] ### Token Claims Design ```json { "sub": "user_id", "tid": "tenant_id", "roles": ["role_name"], "scope": "api:access", "iat": 1700000000, "exp": 1700003600 } ``` ### Core Policy Implementation [Code block: SQL schema, Rego policy, or SpiceDB schema as appropriate] ### Enforcement Point Code [Code block: middleware or decorator implementation in the user's language] ### List Endpoint Strategy [Description of query rewriting or lookup approach for collection filtering] ### Testing Checklist - [ ] Permission matrix tests (all role × action combinations) - [ ] Cross-tenant isolation tests - [ ] Deactivated principal tests - [ ] Privilege escalation tests - [ ] Policy version logging verified - [ ] Authorization audit log format confirmed ### Known Trade-offs and Risks [Specific risks for the chosen approach and mitigations] ``` --- ## Rules 1. **NEVER scatter `if user.role == X` checks directly in business logic.** All authorization decisions must pass through a single `authorize(principal, resource, action)` boundary. Business logic that contains role checks cannot be tested in isolation and creates authorization drift. 2. **NEVER trust the tenant ID from the request body or URL parameters alone.** Always verify the resource's actual `tenant_id` (fetched from the database) matches the `tid` claim in the authenticated token. Skipping this check is the #1 cause of cross-tenant data leakage in multi-tenant SaaS. 3. **NEVER embed dynamic permission lists in JWT tokens.** Tokens cannot be invalidated mid-flight. If a user's permissions change (role revoked, account suspended), a token with embedded permission claims will continue to grant access until expiry. Put the user ID and roles in the token; look up fine-grained permissions at decision time from a fast store. 4. **ALWAYS return 403 Forbidden, not 404 Not Found, when a resource exists but the caller lacks permission** -- unless the existence of the resource is itself sensitive (e.g., a private user profile). Document any intentional 404-for-auth responses in the API contract. Using 404 silently hides resources from unauthorized users but misleads authorized users who mistype an ID. 5. **NEVER evaluate authorization after the data has already been returned.** The pattern `fetch_resource() → check_permission() → return or error` is wrong because the database round-trip has already occurred. The correct pattern is `check_permission() → fetch_resource() → return` or use query-level filtering so unauthorized records are never loaded. 6. **ALWAYS log the policy version, not just the decision.** Authorization audit logs must contain: `timestamp`, `principal_id`, `resource_id`, `action`, `decision` (allow/deny), `policy_version`, `decision_latency_ms`. Without the policy version, you cannot reproduce a historical authorization decision during an audit. 7. **NEVER use RBAC with more than 50 roles.** Above ~50 roles, the cognitive overhead of role management exceeds the overhead of a simple ABAC policy. Role explosion is a failure mode, not a feature. If you're approaching 50 roles, refactor to ABAC with attribute conditions. 8. **ALWAYS cache authorization decisions with a bounded TTL.** For a centralized PDP, uncached decision latency (5-50ms) will compound across microservice call chains. Use Redis with a 30-60 second TTL keyed on `(principal_id, resource_id, action)`. Invalidate immediately on role change events via a pub/sub channel. 9. **NEVER conflate service-to-service authorization with user authorization.** Internal service calls use mTLS certificates or signed service tokens (not user JWTs). Define a separate principal type for services with its own permission set. A user JWT presented by a compromised internal service should not grant elevated access. 10. **ALWAYS write authorization denial reasons to server-side logs, but return only generic denial messages to clients.** The client receives `{"error": "forbidden"}`. The server log records `denied: user lacks document:write on document_id=X (user has roles: [viewer], required: [editor, admin])`. Returning detailed denial reasons to clients enables permission enumeration attacks. --- ## Edge Cases ### Permission Cache Stale After Role Revocation When an administrator revokes a user's role (e.g., offboarding, security incident), the user's cached authorization decisions remain valid until TTL expiry -- potentially 30-60 seconds with standard caching. For high-security events (account termination, suspected compromise), implement an **emergency revocation path**: push the user ID to a Redis blocklist set with TTL equal to the maximum token lifetime. Every authorization middleware checks this blocklist before the permission cache. This blocklist check must be fast (a single Redis GET) and must not be skippable. ### Multi-Tenant Policy Isolation with ABAC When multiple tenants can author their own policies (policy-as-configuration for enterprise customers), policy namespacing is critical. Each tenant's policies must execute in a sandboxed context with access only to their own data slice. In OPA, use bundle namespacing: tenant-specific policies live under `data.tenants.{tenant_id}.authz` and cannot reference `data.tenants.{other_id}`. Validate all tenant-authored policies against a meta-policy (policy about what policies are allowed to do) before activation. Reject any tenant policy that uses `http.send`, file I/O, or references cross-tenant data paths. ### Hierarchical Resource Permissions (Inheritance)
Ver en GitHub
Este SKILL.md es muy grande, por eso SkillsMP muestra aqui solo la primera seccion. Ver en GitHub