← Back to Library

How Uber reinvented access control for microservices

From Simple Rules to Smart Policies

Uber's microservices infrastructure processes millions of authorization decisions every day. Each Application Programming Interface call, database query, and Kafka message requires an access check, and those checks must resolve in microseconds. The ByteByteGo team lays out how Uber's engineering organization outgrew basic access control and built something far more flexible in its place.

The system Uber built is called Charter, a centralized policy repository that distributes authorization rules to individual services. At its foundation, every authorization request reduces to a single question: can an actor perform an action on a resource in a given context? Actors are identified using the Secure Production Identity Framework for Everyone (SPIFFE) format, resources use Uber Object Name (UON) URIs, and actions cover the standard create, read, update, and delete operations plus custom verbs like "invoke" and "subscribe."

Uber's infrastructure runs on thousands of microservices, each making authorization decisions millions of times per day. This includes every API call, database query, and message published to Kafka.

The early policy model was straightforward. A YAML file would declare that service-bar could invoke a method on service-foo, or that employees in a particular group could read and write certain reports. Simple, declarative, easy to audit.

How Uber reinvented access control for microservices

But simple is not the same as sufficient.

When Identity Is Not Enough

Three scenarios exposed the limitations of basic policies. The first involved payment support: customer service representatives needed access to payment records, but only for customers in their assigned geographic region. The second was employee data, where workers should be able to edit their own profiles while managers could also view them. The third concerned analytics reports restricted to users belonging to multiple groups simultaneously.

The basic policy syntax can only specify that a representative can access a payment profile by its UUID. It cannot express the requirement that the rep's region must also match the customer's region.

None of these could be expressed with the original model. The policies could say who you are and what you can touch, but they could not reason about relationships between the requester and the resource. Uber needed Attribute-Based Access Control (ABAC).

Bolting Conditions Onto Policies

ABAC extends the basic model by adding a condition field to each permission. A condition is a Boolean expression evaluated against attributes drawn from four categories: actor attributes like department or location, resource attributes like owner or sensitivity level, action attributes, and environment attributes such as the current timestamp or request IP address.

A condition is a Boolean expression that evaluates to true or false based on attributes. If a permission includes a condition, that permission only grants access when the condition evaluates to true.

The sources that provide attribute values at runtime are called attribute stores, or in formal authorization terminology, Policy Information Points. Uber defined four interfaces for these stores: ActorAttributeStore, ResourceAttributeStore, ActionAttributeStore, and EnvironmentAttributeStore. Each store declares which attributes it can provide via a SupportedAttributes function, allowing the authorization engine to validate conditions at compile time rather than failing at runtime.

The design allows a single service to use multiple attribute stores, and a single attribute store can be shared across multiple services for reusability.

This is a clean separation of concerns. Policy authors write conditions. Service owners implement attribute stores. The authorization engine wires them together.

Why Google's Expression Language Won

Rather than inventing a domain-specific language, Uber's engineers evaluated existing open-source options and chose the Common Expression Language (CEL), developed by Google. CEL offered a familiar syntax, support for strings, numbers, booleans, and lists, plus built-in functions for common operations.

Expression evaluation typically takes only a few microseconds. Both Go and Java implementations of CEL are available, meeting Uber's backend service requirements.

The performance profile mattered enormously. With millions of authorization checks per day, even a few extra milliseconds per evaluation would compound into real latency. CEL's lazy attribute fetching was a particularly smart choice: the engine only requests the attribute values actually needed to evaluate a given expression, skipping unnecessary calls to attribute stores.

One counterpoint worth noting: adopting CEL means Uber's security policies are now expressed in a language that most security auditors and compliance teams will not know. The article does not address how non-engineering stakeholders review or approve these condition expressions, which is a gap in the governance story.

One Policy to Rule Thousands of Kafka Topics

The Kafka use case is where ABAC's value becomes most concrete. Uber operates thousands of Kafka topics for event streaming, and each topic needs access controls specifying which services can publish and which can subscribe. Managing individual policies for every topic would have been impractical.

Using ABAC, the Uber engineering team created a single generic policy that applies to all Kafka topics.

The solution relies on a service called uOwn that tracks ownership and roles for technological assets. A single wildcard policy covers every Kafka topic, with a CEL condition checking whether the requesting employee belongs to any Active Directory group that holds the "Develop" role for that specific topic. When ownership changes in uOwn, authorization adjusts automatically. No policy updates. No code deployments.

Instead of managing thousands of individual policies, they maintain one generic policy. As ownership changes in uOwn, authorization automatically adjusts without any policy updates.

This is elegant, though it raises a question the article leaves unanswered: what happens when uOwn itself is wrong? A single point of truth for ownership means a single point of failure for access control. If stale or incorrect data in uOwn grants access that should have been revoked, the wildcard policy amplifies that error across every Kafka topic simultaneously.

The Numbers

Since implementing ABAC, 70 Uber services have adopted attribute-based policies. The article reports that authorization decisions still complete in microseconds despite the added complexity of condition evaluation and attribute fetching. Local evaluation through the authfx library keeps network round-trips out of the critical path.

A single well-designed ABAC policy can govern authorization for thousands or even millions of resources.

Seventy services is a meaningful adoption number, but it is worth contextualizing against Uber's "thousands of microservices." That suggests ABAC adoption is still in its early stages, covering perhaps a few percent of the total service fleet. Whether the remaining services do not need ABAC or simply have not migrated yet is left unsaid.

Bottom Line

Uber's Charter system demonstrates a mature approach to a problem that afflicts every large microservices architecture: access control that is both fine-grained and manageable at scale. The progression from basic identity-based policies to attribute-based conditions is a well-trodden path in enterprise security, but the execution details matter. Choosing CEL over a custom language, distributing policies locally through authfx rather than making remote calls, and integrating with existing ownership systems like uOwn all reflect practical engineering judgment.

The article is most valuable as a reference architecture. Teams building their own authorization systems will find the four-store attribute model, the CEL integration pattern, and the Kafka wildcard policy particularly instructive. What it does not cover, notably operational challenges like debugging denied requests, handling attribute store failures gracefully, and managing policy migrations, would make for an equally important follow-up.

Deep Dives

Explore these related deep dives:

Sources

How Uber reinvented access control for microservices

Don’t miss out: your free pass to Monster SCALE Summit is waiting! 50+ engineering talks on AI, databases, Rust, and more. (Sponsored).

Monster SCALE Summit is a new virtual conference all about extreme-scale engineering and data-intensive applications.

Join us on March 11 and 12 to learn from engineers at Discord, Disney, LinkedIn, Uber, Pinterest, Rivian, ClickHouse, Redis, MongoDB, ScyllaDB and more. A few topics on the agenda:

What Engineering Leaders Get Wrong About Scale

How Discord Automates Database Operations at Scale

Lessons from Redesigning Uber’s Risk-as-a-Service Architecture

Scaling Relational Databases at Nextdoor

How LinkedIn Powers Recommendations to Billions of Users

Powering Real-Time Vehicle Intelligence at Rivian with Apache Flink and Kafka

The Data Architecture behind Pinterest’s Ads Reporting Services

Bonus: We have 500 free swag packs for attendees. And everyone gets 30-day access to the complete O’Reilly library & learning platform.

Uber’s infrastructure runs on thousands of microservices, each making authorization decisions millions of times per day. This includes every API call, database query, and message published to Kafka. To make matters more interesting, Uber needs these decisions to happen in microseconds to have the best possible user experience.

Traditional access control could not handle the complexity. For instance, you might say “service A can call service B” or “employees in the admin group can access this database.” While these rules work for small systems, they fall short when you need more control. For example, what if you need to restrict access based on the user’s location, the time of day, or relationships between different pieces of data?

Uber needed a better approach. They built an attribute-based access control system called Charter to evaluate complex conditions against attributes pulled from various sources at runtime.

In this article, we will look at how the Uber engineering team built Charter and the challenges they faced.

Disclaimer: This post is based on publicly shared details from the Uber Engineering Team. Please comment if you notice any inaccuracies.

Understanding the Authorization Request.

Before diving into ABAC, you need to understand how Uber thinks about authorization. Every access request can be broken down into a simple question:

Can an Actor perform an Action on a Resource in a given Context?

Let’s understand each component of this statement:

Actor represents the entity making the request. At Uber, this could be an employee, a customer, or another microservice. Uber uses the SPIFFE format to identify actors. An ...