Authorization Patterns
What is Authorization
Read aloud is unavailable in this browser.
The Gatekeeper's Dilemma
Imagine a massive corporate headquarters with thousands of employees. At the entrance, security guards verify each person's identity—checking badges, comparing faces to photos, perhaps even requiring a fingerprint scan. This is authentication: proving you are who you claim to be.
But once inside the building, a new challenge arises. Can this employee access the executive boardroom? Can they view payroll data? Can they modify the production database? Can they deploy code to live systems? This is authorization: determining what an authenticated entity is allowed to do.
In modern distributed systems, authorization is one of the most critical—and most commonly misunderstood—security concerns. A single authorization bypass can expose sensitive data to unauthorized users, allow privilege escalation attacks, or enable complete system compromise. Yet authorization is also extraordinarily complex, involving nuanced business rules, dynamic contexts, and intricate permission hierarchies that must be enforced consistently across dozens or hundreds of microservices.
By the end of this page, you will understand authorization as a distinct security domain, know how it differs from authentication, grasp the core concepts and terminology, and appreciate why authorization is particularly challenging in distributed architectures. This foundation is essential before exploring specific authorization patterns.
Authentication vs Authorization: The Fundamental Distinction
Before diving into authorization patterns, we must establish an absolutely clear distinction between authentication and authorization. These terms are frequently confused, abbreviated identically (AuthN vs AuthZ), and sometimes incorrectly used interchangeably—even by experienced engineers.
Authentication (AuthN) answers the question: "Who are you?"
Authentication establishes identity. It verifies that a user, service, or system is genuinely who or what it claims to be. Authentication mechanisms include passwords, biometrics, cryptographic certificates, API keys, and multi-factor authentication. The output of authentication is an identity assertion—a claim that "this request comes from User X" or "this service is Service Y."
Authorization (AuthZ) answers the question: "What are you allowed to do?"
Authorization determines permissions. Given a verified identity, authorization decides whether that identity can perform a specific action on a specific resource. Authorization mechanisms include role-based access control, attribute-based policies, access control lists, and capability tokens. The output of authorization is a permit/deny decision for a specific operation.
| Aspect | Authentication (AuthN) | Authorization (AuthZ) |
|---|---|---|
| Core Question | Who are you? | What can you do? |
| Input | Credentials (passwords, tokens, certificates) | Identity + Resource + Action |
| Output | Verified identity or rejection | Permit or deny decision |
| Timing | Usually at session start or request initiation | Every protected operation |
| Failure Mode | Login failure, invalid credentials | Access denied, forbidden |
| HTTP Status Code | 401 Unauthorized | 403 Forbidden |
| Example Decision | "Is this really Alice?" | "Can Alice delete this file?" |
| State | Often stateless (JWT) or session-based | Can be stateless or require context lookup |
| Frequency | Once per session or per request | Multiple times per request possible |
Many security breaches occur when developers conflate authentication with authorization. A system might correctly verify that a request comes from an authenticated user, but then fail to check whether that user has permission to perform the requested action. This results in broken access control—consistently ranked among the top web application security risks by OWASP.
The dependency relationship:
Authorization typically depends on authentication (you must know who someone is before deciding what they can do), but authentication never depends on authorization. This creates a natural flow:
Request → Authentication → Identity Established → Authorization → Permission Decision → Action Executed
However, this linear view oversimplifies reality. In complex systems, a single request might require multiple authorization decisions at different layers, and some authorization contexts might not require user authentication at all (e.g., anonymous users with limited permissions, or machine-to-machine communication with service identities).
Core Authorization Concepts and Terminology
Authorization systems revolve around a set of fundamental concepts that appear regardless of the specific pattern or implementation. Understanding this vocabulary is essential for designing and discussing authorization architectures.
- •Subject (Principal) — The entity requesting access. Subjects can be users, service accounts, applications, or any other identity that performs actions. In code, this is often the "who" extracted from an authentication token.
- •Resource (Object) — The entity being accessed. Resources can be data objects (files, records), system components (APIs, databases), or abstract entities (reports, workflows). Resources are the "what" that access is being requested to.
- •Action (Operation) — The operation the subject wants to perform on the resource. Common actions include read, write, delete, execute, and approve. More granular systems define domain-specific actions like "transfer_funds" or "approve_deployment."
- •Permission — A specific grant that allows a subject to perform an action on a resource. Permissions are the atomic units of access control. They can be expressed as tuples: (subject, action, resource) or more complex rule structures.
- •Policy — A rule or set of rules that define how authorization decisions are made. Policies can be simple ("admins can do anything") or complex ("users can edit documents they created, during business hours, if the document isn't locked").
- •Context (Environment) — Additional information beyond subject, action, and resource that affects authorization decisions. Context includes time of day, IP address, device type, risk scores, and request metadata.
- •Decision (Effect) — The outcome of an authorization evaluation: permit, deny, or sometimes not-applicable. Some systems support additional effects like "permit with obligations" or "deny with reason."
The Authorization Question:
Every authorization decision can be framed as answering this question:
"Given subject S, can S perform action A on resource R, within context C?"
This is sometimes called the authorization tuple: (S, A, R, C) → {permit, deny}
Different authorization patterns provide different mechanisms for answering this question:
- Access Control Lists (ACLs) attach permissions directly to resources
- Role-Based Access Control (RBAC) groups permissions into roles assigned to subjects
- Attribute-Based Access Control (ABAC) evaluates policies against attributes of subjects, resources, and context
- Relationship-Based Access Control (ReBAC) derives permissions from relationships between entities
Each approach has distinct advantages for different use cases, as we'll explore in subsequent pages.
When designing authorization logic, always think in terms of the complete tuple: WHO is trying to do WHAT to WHICH resource under WHAT circumstances. Incomplete reasoning—like "admins can do this"—often leads to security gaps because it doesn't specify the action or resource scope.
The Authorization Data Model
Effective authorization requires careful data modeling. The relationships between subjects, resources, permissions, and policies form the foundation of any access control system. Let's examine the key modeling decisions and patterns.
Subject Identity Model:
Subjects in an authorization system typically include:
- Users — Human beings with accounts, potentially from multiple identity providers
- Groups — Collections of users that share common access needs
- Service Accounts — Machine identities for automated processes and services
- API Clients — External applications authorized to call your APIs
- Anonymous — Unauthenticated actors with baseline permissions
Subjects can have multiple concurrent identities (a user might be part of several groups, have multiple roles, and be accessing from different devices). The authorization system must resolve these overlapping identities into coherent permission sets.
Resource Hierarchy Model:
Resources are rarely flat—they exist in hierarchies that affect permission inheritance:
Organization
├── Team A
│ ├── Project X
│ │ ├── Document 1
│ │ └── Document 2
│ └── Project Y
│ └── Document 3
└── Team B
└── Project Z
└── Document 4
Key modeling questions include:
- Do permissions granted at parent level automatically apply to children?
- Can child resources override or restrict parent permissions?
- How deep can the hierarchy go before performance degrades?
- How do you handle resources that belong to multiple parents?
Permission Granularity:
Permissions can be modeled at varying levels of granularity:
Coarse-grained: admin, editor, viewer
- Pros: Simple to understand and manage
- Cons: Limited flexibility, over-privileged users
Fine-grained: documents:read, documents:write, documents:delete, documents:share
- Pros: Precise access control, principle of least privilege
- Cons: Complex to manage, permission explosion
Instance-level: documents:12345:read, documents:12345:write
- Pros: Per-resource access control
- Cons: Massive permission data, performance challenges
Most real-world systems use a hybrid approach: coarse-grained roles for general access patterns, with fine-grained permissions for sensitive operations and instance-level controls for high-value resources.
As systems grow, the number of possible (subject, action, resource) combinations explodes combinatorially. With 10,000 users, 50 actions, and 1,000,000 resources, you have 500 billion possible permission combinations. No system can store or evaluate this directly—authorization patterns are essentially compression and caching strategies for this vast permission space.
Authorization in Distributed Systems
Authorization becomes significantly more complex in distributed architectures. What seems straightforward in a monolithic application—checking if a user can perform an action—becomes a distributed systems challenge when microservices are involved.
The Distributed Authorization Challenges:
- •Consistency — Authorization decisions must be consistent across services. If Service A permits an action, Service B shouldn't deny it for the same request. But maintaining strong consistency across distributed services conflicts with availability.
- •Latency — Every authorization check adds latency to requests. If a request touches 10 services and each service makes an authorization call, even 10ms per check adds 100ms of overhead. At scale, this becomes untenable.
- •Data Locality — Authorization decisions often require data from multiple sources: user attributes from an identity provider, resource metadata from a database, policy rules from a configuration store. Gathering this data efficiently is non-trivial.
- •Policy Updates — When authorization policies change, all services must reflect the new rules. Propagating policy updates while maintaining consistency and availability is a classic distributed systems problem.
- •Audit and Compliance — Distributed authorization creates distributed audit trails. Reconstructing who accessed what, when, and why across multiple services requires careful correlation of logs and events.
Architectural Patterns for Distributed Authorization:
| Pattern | Description | Pros | Cons |
|---|---|---|---|
| Gateway Enforcement | Single API gateway handles all authorization | Centralized, simple | Limited context, gateway becomes bottleneck |
| Embedded Enforcement | Each service enforces its own authorization | High performance, full context | Policy sprawl, consistency challenges |
| Sidecar Enforcement | Authorization sidecar proxy for each service | Consistent policy, service isolation | Operational complexity, latency overhead |
| Centralized PDP | All services call central Policy Decision Point | Single source of truth, easy updates | Single point of failure, latency |
| Distributed PDP | Multiple PDP instances with synchronized policies | High availability, good performance | Synchronization complexity, eventual consistency |
The Zero Trust Imperative:
Modern authorization must embrace zero trust principles: never assume a request is authorized just because it came from an internal service or passed through a gateway. Every service should validate authorization independently, even for internal traffic. This defense-in-depth approach prevents lateral movement if any single service is compromised.
This means authorization checks are not optional—they're mandatory at every trust boundary in your architecture.
The Authorization Decision Lifecycle
An authorization decision follows a specific lifecycle from request initiation through decision enforcement. Understanding this lifecycle helps identify where bottlenecks occur and where to optimize.
Phase 1: Request Interception
Authorization begins when a protected resource is accessed. Interception can occur at:
- API Gateway / Ingress controller (edge enforcement)
- Application middleware (framework-level enforcement)
- Service method (code-level enforcement)
- Database layer (row-level security)
The interception point determines what context is available and what latency is acceptable.
Phase 2: Context Assembly
Before making a decision, the authorization system gathers necessary context:
- Subject attributes: User ID, roles, groups, tenant, session data
- Resource attributes: Resource type, owner, classification, state
- Action attributes: Operation type, HTTP method, parameters
- Environmental attributes: Timestamp, IP, device, risk score
Context assembly can require database lookups, cache reads, or calls to external services. Efficient context assembly is often the key to authorization performance.
Phase 3: Policy Evaluation
With context assembled, policies are evaluated to reach a decision:
- Identify applicable policies (based on subject, resource, action)
- Evaluate each policy against the assembled context
- Resolve conflicts if multiple policies apply
- Return final decision: permit, deny, or not-applicable
Policy engines vary from simple if-then rules to sophisticated domain-specific languages like Rego (Open Policy Agent) or Cedar (AWS).
Phase 4: Decision Caching
To reduce latency, authorization decisions are often cached:
- Positive caching: Remember permit decisions to avoid re-evaluation
- Negative caching: Remember deny decisions (controversial—security vs. performance)
- TTL management: Balance freshness with performance
- Invalidation: Clear cache when policies or attributes change
Caching authorization decisions is inherently risky—stale cache entries can grant or deny access incorrectly.
Phase 5: Decision Enforcement
The final phase enforces the decision:
- Permit: Allow the action to proceed
- Deny: Return appropriate error (403 Forbidden) with minimal information
- Audit: Log the decision for compliance and debugging
- Obligations: Execute any required side effects (e.g., log sensitive access)
The fastest authorization decision is one you never have to make. Use coarse-grained checks early ("is user authenticated?") to reject obviously invalid requests before expensive fine-grained authorization. Layer your defenses from cheap to expensive.
Common Authorization Failures and Vulnerabilities
Authorization vulnerabilities are among the most dangerous and most common security flaws. OWASP consistently ranks broken access control as a top-10 vulnerability. Understanding failure modes helps design more robust systems.
- •Insecure Direct Object References (IDOR) — Accessing resources by manipulating identifiers in URLs or parameters without authorization checks. Example: changing
/api/invoices/123to/api/invoices/456to view another user's invoice. - •Privilege Escalation — Gaining access to functionality or data beyond intended permissions. Vertical escalation (user → admin) and horizontal escalation (user A → user B's data) are both critical threats.
- •Missing Function-Level Access Control — Assuming that hiding a UI element hides the underlying functionality. Attackers can directly call API endpoints even if the UI doesn't show them.
- •Authorization Bypass via Parameter Manipulation — Modifying request parameters to circumvent authorization. Example: adding
isAdmin=trueto a request body that's blindly trusted. - •Race Conditions in Authorization — Time-of-check to time-of-use (TOCTOU) vulnerabilities where authorization state changes between check and use.
- •Forced Browsing — Accessing resources that exist but aren't linked, such as
/admin/endpoints that lack authorization checks because "nobody knows the URL." - •Mass Assignment — Updating protected fields by including them in API requests, bypassing authorization on specific attributes.
Real-World Authorization Breach Example:
In 2021, a major social media platform experienced a data exposure incident where users could access private photos of other users by incrementing numeric photo IDs in API calls. The system authenticated users correctly but failed to verify that the requesting user had authorization to view each specific photo. This is a classic IDOR vulnerability.
The fix required implementing proper resource-level authorization: before returning any photo, verify that the requesting user has a relationship (friend, follower, owner) that grants access. This seemingly simple check requires efficient relationship queries at scale—millions of authorization checks per second.
Defensive Principles:
- •Deny by default — Require explicit grants; never assume access is permitted
- •Centralize authorization logic — Avoid scattered permission checks throughout code
- •Validate on the server — Never trust client-side authorization enforcement
- •Check authorization at every layer — Gateway, service, data access
- •Use indirect references — Map user-visible IDs to internal IDs after authorization
- •Log all authorization decisions — Both permits and denials for audit trails
- •Regularly audit access — Review who has access to what and why
Authorization Enforcement Points
Authorization enforcement can occur at multiple layers of a system architecture. Each layer offers different trade-offs between security, performance, and development complexity. Defense-in-depth recommends enforcement at multiple points.
| Layer | What It Protects | Advantages | Limitations |
|---|---|---|---|
| Network/Firewall | Network access, service-to-service communication | Very fast, blocks at network level | Coarse-grained, no user context |
| API Gateway | External API access, route-level permissions | Centralized, consistent for all services | Limited request context, can't protect internal traffic |
| Service Mesh | Service-to-service, mTLS, basic policies | Infrastructure-level, transparent to apps | Policy limitations, latency overhead |
| Application Middleware | HTTP endpoints, request-level authorization | Rich context, framework integration | Duplicated across services, developer dependent |
| Business Logic | Domain operations, fine-grained actions | Full context, complex rules possible | Scattered logic, hard to audit |
| Data Access Layer | Row/column level security, query filtering | Prevents data leakage at source | Database-specific, limited policy expressiveness |
Layered Enforcement Example:
Consider a healthcare application where doctors access patient records:
- Network layer: Firewall allows only authorized IP ranges to reach the service
- API Gateway: Validates JWT tokens, ensures user is authenticated
- Service middleware: Verifies user has 'physician' role
- Business logic: Confirms physician is assigned to this patient's care team
- Data layer: Row-level security filters records to only those the physician can see
An attacker would need to bypass all five layers to access unauthorized records. This defense-in-depth approach significantly raises the bar for successful attacks.
A common anti-pattern is relying solely on API gateway authorization, assuming internal services are "trusted." This fails when: (1) an internal service is compromised, (2) traffic bypasses the gateway, or (3) services are exposed directly. Always enforce authorization at the service level, not just the edge.
KEEP LEARNING
There’s more to understand.
You’re reading the introduction. Create a free account to read the complete lesson, save notes and keep track of your progress.