This post is part of a series.
- Part 1: What’s Wrong with the Current OWASP Microservice Security Cheat Sheet?
- Part 2: Updating OWASP’s Microservice Security Cheat Sheet: Core Concepts (this post)
In my first post, I laid out what’s broken in the OWASP Microservice Security Cheat Sheet and why it matters. Now it’s time to move from critique to contribution. This post kicks off the hands-on part of the series: I’ll outline a foundational reference architecture for authorization, explain the first-party and third-party use cases I previously mentioned but didn’t yet explore, and set the stage for what’s to come.
The goal of the series is to create guidance that’s clear, actionable, and grounded in real-world needs. But that only works with community input. Your feedback and suggestions will help shape a more practical and useful update. So let’s dive in and build it together.
One quick note before we begin: naming is hard, and the world of authentication and authorization is no exception. Many terms are overloaded or misleading. Take HTTP’s 401 Unauthorized
, for example. Despite the name, it’s actually an authentication error, returned when credentials like a username or password are missing or invalid. The 403 Forbidden
status is reserved for true authorization failures. Confusing, right? To avoid this kind of confusion and make discussions clearer, I’ve tried to establish a consistent and understandable vocabulary throughout this series — a kind of “ubiquitous language”, if you like — to make the topic easier to follow and reason about. If something doesn’t make sense or feels off, please bear with me. Either way, I’d love to hear your feedback — as with the previous post, you can share your thoughts by commenting on my LinkedIn posts, sending me a direct message there, or joining the discussion in heimdall’s Discord server in the #off-topic channel.
Introduction
The microservice architecture is increasingly used to design and implement application systems in both cloud-based and on-premise environments, particularly for high-scale applications and services. However, it introduces a range of security challenges that must be addressed during both the design and implementation phases.
Two of the most critical security concerns are authentication and authorization. It is therefore essential for everyone involved in designing, developing, and operating microservices-based systems to understand and correctly apply architectural patterns that address these concerns.
The goal of this cheat sheet is to describe common authentication and authorization patterns, highlight their trade-offs, and provide actionable recommendations. It also outlines common pitfalls to avoid when applying these patterns in practice.
Authorization Reference Architecture
The current cheat sheet presents a reference architecture only within the section on service-level authorization patterns. This version elevates it to a dedicated, central section to emphasize its foundational importance and to provide a clearer, consistent basis for discussions that will follow. Notably, the detailed component descriptions remain largely unchanged.
To lay the foundation for the patterns described in this cheat sheet, this section introduces the general building blocks of an authorization system, based on NIST SP 800–162. While that standard focuses on Attribute-Based Access Control (ABAC), the conceptual roles it defines are relevant to nearly any access control system.
These roles are:
- Subject: An active entity (e.g., a user, application, or device) that attempts to perform an action on an Object.
- Object: A passive entity (e.g., a file, an insurance record, or a blog article), that is the target of an action attempted by the Subject.
- Policy: A set of rules that define who is allowed to do what under which conditions — for example, “Managers can approve expenses under $1000”, or “Users can access documents they own”. Policies are evaluated at runtime using attributes of the user, the resource, and the context (such as time, or location).
- Policy Enforcement Point (PEP): The component that intercepts a request and enforces the outcome of an authorization decision, allowing or denying the request.
- Policy Decision Point (PDP): Evaluates policies and computes authorization decisions based on the incoming request and available data.
- Policy Information Point (PIP): Supplies attribute data or contextual information that the PDP requires to evaluate a policy.
- Policy Administration Point (PAP): Allows management of access control policies by providing tools for authoring, testing, and maintaining them.
A Story to Ground the Concepts
This is a new addition aimed at illustrating the architectural roles through a concrete example. Drawing on a simple, relatable scenario helps bridge abstract components to real-world understanding.
Imagine Alice wants to read an article on her favorite blog platform. In this story:
- Alice is the subject.
- The article she wants to read is the object.
- The action (“read”) is what she wants to perform.
- Her browser sends a request on her behalf to the platform’s backend services.
Let’s break down what happens step by step:
Each time Alice interacts with the platform — by clicking a link, submitting a form, or opening a page — a request is made to one or more backend services. These services must decide: Can Alice do this? and more subtly: What exactly is Alice allowed to do in this context?
That decision process starts with the Policy Enforcement Point (PEP). Think of the PEP as a gatekeeper — it sees the request and knows it must enforce some kind of access control. But it doesn’t contain the logic to decide what’s allowed. Instead, it delegates that to the Policy Decision Point (PDP).
The PDP evaluates the request against a set of policies. These policies might include conditions like:
- Alice must be logged in.
- Alice must have an active subscription.
- Alice can only read the full article if her subscription level is “Premium”.
To perform this evaluation, the PDP often needs more information than what’s in Alice’s request. This is where the Policy Information Point (PIP) comes in. In this case, one PIP is involved: a user management service that provides Alice’s subscription information. This PIP supplies the PDP with the information needed to evaluate the aforesaid policies.
But here’s where a crucial detail often gets missed: the PDP doesn’t always answer a closed question like “yes” or “no”. In many cases, the PDP may answer open questions with answers like:
- Alice can read the article, but only the excerpt.
- Alice can read the full article if her subscription is “Premium” or if the article is marked as public.
- Alice can read up to three full articles per day on a free plan.
- Alice can read that particular set of articles
In these cases, the PDP returns a decision along with additional access context information that describes how access is permitted, and which additional actions to perform. This might include details like which parts of a resource are visible or what usage limits apply. For example, if Alice is on a free plan and has already read three full articles today, the PDP might return a “permit” decision along with structured attributes specifying that only the excerpt of the requested article should be shown.
The PEP then takes that decision and enforces it, meaning the request is either allowed to proceed to the protected resource or is blocked. Enforcement is binary: permit or deny. But if the decision includes additional data — like an instruction to show only the excerpt — it’s up to downstream components to interpret and act on that. In Alice’s case, this means shaping the response to include only the article excerpt.
There is, however, one essential prerequisite: the system must know who the subject is — that is, it must verify the subject’s identity, confirming that Alice is indeed Alice. This verification process is the domain of authentication. Without it, the PEP has no basis on which to enforce access decisions. Authentication is therefore foundational, which is why we begin by examining authentication patterns and approaches. But before doing that, there is a need to explore a few essential concepts that provide context for understanding the broader landscape of authentication and authorization.
Policy Representations and Lifecycle
This section introduces a topic not really covered in the current cheat sheet, but which is essential for the further discussions.
As described above, authorization policies define who can do what under which conditions. Depending on how they’re represented and integrated into a system, their impact on development, operations, and security can vary widely. In practice, policies are implemented in two main ways:
-
Hardcoded policies: These are embedded directly in application code — for example, conditional checks like
if user.role == 'admin'
. In this model, the PDP is implicit within the application logic, and the PEP might be an interceptor, handler, or a simple conditional branch. - Declarative policies: These are defined outside the application code in structured formats, evaluated by a dedicated PDP. Examples include policies written in Rego, Cedar, XACML, or other authorization languages. This model clearly separates the enforcement logic (PEP) from decision logic (PDP) and externalizes policy definition.
Declarative policies allow the policy lifecycle to be managed independently of the application lifecycle. This separation has several operational and organizational benefits:
- Access policies reflect business rules, regulatory obligations, or security requirements — and while these are also drivers for application features, their rate of change, ownership, and scope typically differ:
- Regulatory updates may require immediate changes to access conditions without modifying the underlying feature set.
- Security incident response might require temporary or permanent changes to access controls outside a normal release cycle.
- Declarative policies empower non-developers to request or implement access changes (e.g., enabling partner access during a pilot program) without waiting for a full development cycle or redeployment, especially when the changes don’t require altering core application logic.
- Policies may vary in scope or depth, with some governing access across multiple services or applications, while others are more narrowly focused.
When access policies are tightly coupled to code, any change — no matter how urgent or isolated — requires a code change, test cycle, and deployment. Separating policy from application code allows organizations to react faster and more safely to changes, without compromising the integrity of the software development process.
- Policies are high-stakes — access control bugs are different from feature bugs. They tend to be catastrophic, not just annoying:
- Granting access when you shouldn’t can lead to data breaches
- Revoking access incorrectly can break business processes
Externalized policies are easier to review, audit, and test — just like any other configuration artifact. They can also be subject to staged rollouts and automated validation. Enabling and governing these capabilities is the responsibility of the Policy Administration Point (PAP), which orchestrates the authoring, validation, and controlled distribution of policies. Policies themselves are also subject to access controls. In that sense, the PAP also incorporates aspects of the PEP and the PDP — but is focused entirely on the policy lifecycle rather than on business-related decisions.
- Declarative policies also enable before-the-fact audit — the ability to answer “Who currently has access to this object/resource?” without needing to wait for a request to happen or instrument code. This reverse-query capability is essential for governance, compliance, and risk assessments, and is practically impossible when policies are hardcoded and scattered across multiple applications.
First-Party vs. Third-Party
This new section addresses a critical distinction that was missing. Clarifying when authentication and authorization patterns apply to first-party versus third-party contexts helps prevent misapplication of protocols and approaches.
A key distinction in access control scenarios lies in on whose behalf the subject is acting. This determines whether we’re dealing with a first-party or a third-party context.
In a first-party scenario, the subject acts on their own behalf — for instance, when Alice reads or edits articles in her account. In a third-party scenario, the subject is acting on behalf of someone else — like when Alice delegates access to an external service that analyzes her articles. The third-party service becomes the subject, while Alice remains the principal authorizing access.
This distinction has important implications for how delegation is modeled, how trust is established, and what guarantees are needed from the involved systems.
Between these scenarios, there is also a special case within the first-party context, where a user explicitly authorizes a trusted internal service to act on their behalf. In this situation, the user acts both as the subject (requesting the action) and as the PDP by giving explicit consent. This typically occurs within trusted domains, where user approval initiates actions executed by internal services. For example, in a banking app, the user approves a transaction, while the banking system acts as the PEP. These interactions rely on existing authentication and authorization mechanisms and are enhanced by dedicated protocols to ensure integrity and non-repudiation. Additionally, other PDPs within the system may apply further controls, such as fraud detection, compliance verification, or transaction limits, before final enforcement.
While exploring these contexts, it’s important to understand that different protocols address different needs. Some protocols are tailored to the first-party context only, such as Security Assertion Markup Language (SAML) or Central Authentication Service (CAS), which are primarily designed for direct user authentication and carry attributes for authorization purposes within trusted domains. Others, like Open Authorization (OAuth 2.0), focus exclusively on the third-party context, enabling delegated access to resources on behalf of another party. And then there are protocols like OpenID Connect (OIDC) that support both contexts, combining identity information with delegated access.
What all these protocols have in common is that they define mechanisms to authenticate the involved parties. However, the details of how this authentication is performed - e.g., through passwords, or by making use of other factors - are not covered in this cheat sheet. Likewise, the protocols themselves are not the focus here; there are excellent existing cheat sheets for that purpose (which we will reference). Instead, this document emphasizes patterns: how different approaches to authentication and authorization are architecturally applied, and what implications they carry.
On Subjects, Principals and Identities
This section is new and addresses an important distinction that is absent from the current cheat sheet — the difference between subjects, principals, and identities.
Note: You’ll find these terms in various standards — like NIST SP 800–63, NIST SP 800–162, and ITU-T X.800 — but they’re often used inconsistently or even interchangeably. From my experience, this inconsistency tends to cause more confusion than clarity. The definitions provided here reflect how these concepts typically manifest in real-world systems, especially those we design and operate today.
Another important topic to understand before we explore authentication and authorization patterns is the concept of a subject. According to the reference architecture and the story above, a subject is an active entity that carries an identity and is the target of authentication.
However, in most real-world systems, authentication is not limited to a single type of active entity. Instead, there are often multiple forms of identity involved, each representing a different kind of actor or context:
- End-users: Human users interacting with a system via e.g. a browser, or a mobile app.
- Devices: The user’s device (e.g., smartphone, laptop, or IoT hardware), which may have its own identity.
- External clients: Applications or scripts accessing an API on behalf of a user or system.
- Internal workloads: Services or components within a distributed system communicating with each other.
All of these are principals — identifiable entities that can be authenticated and authorized. A subject, in turn, may consist of one or more such principals. For example, a request from a mobile app may involve both the authenticated user and the device they’re using. In a service-to-service call, the subject might be the internal service identity, optionally carrying along delegated user context.
Importantly, the definition of a subject is often shaped by the perspective of the PDP that evaluates the request. Each PDP — or even each policy — may view the subject differently, based on what attributes or entities are relevant for its decision-making. One policy may only care about the identity of the user. Another may treat the combination of user and device as the subject. A third may include the client application or network context as additional principals. In this sense, a subject is not a fixed notion, but a context-dependent composition of principals as seen by the evaluating component.
Understanding subjects in this compositional and context-sensitive way is key to interpreting the patterns described in this cheat sheet. While many patterns focus on a single principal type (e.g., user or service), they often support multi-principal subjects through identity propagation and proper orchestration of authentication mechanisms.
The last remaining concept to cover is identity. An identity is a collection of attributes that uniquely identify an entity, similar to a primary key in a database. In some cases, this might be a single attribute such as an ID, while in others it can be a combination of several attributes. Unlike subjects and principals, which refer to active entities or actors, the concept of identity also applies to passive entities — the objects — as well.