The Architectural Challenge of Authorization
[This article first appeared on The New Stack].
Most software developers think of Authentication as a solved problem. We can rely on a mature set of standards, such as OAuth2, OIDC, SAML, and JWT, and libraries in every language that make it easy to implement authentication. Developer APIs from vendors such as Auth0, Okta, and others make this even easier.
What about Authorization? Just like authentication, every B2B SaaS application requires an authorization system to enforce permissions that are granted to different tiers of users. But compared to authentication, authorization is a harder problem and is far from solved.
Developers want it
There’s a clear demand for a turnkey developer API for authorization: in over 80 conversations with the technical leaders of startups from seed-stage to series D, the vast majority think of authorization as “undifferentiated heavy lifting” that is very important to get right, yet doesn’t add any unique value to the users of their applications. They’d rather rely on a turnkey solution (like Stripe for payments, or Twilio for text messages), but haven’t found one that works for them.
Yet the patterns that these startups use are remarkably similar: they define a set of permissions that is the cartesian product of the operations (“verbs”) and resources (“nouns”) in their domain. They define access control rules based on roles (RBAC), attributes (ABAC), and scopes/permissions that come from the user’s profile.
80% of what they have to do isn’t unique to their application domain, but engineering teams find themselves on a continuous feature ramp as their customers ask for: finer-grained permissions, custom roles, more sophisticated mappings between permissions and user attributes, integration with more identity providers, and a complete audit trail of authorization logs.
So why isn’t there a “Stripe for authorization” -- a developer API that is delivered as a service, that makes all of this pain go away?
An architectural challenge
Unlike authentication, which only happens at the beginning of an application session, a well-architected application will make one or more authorization decisions during the processing of each application request. Unlike developer APIs for sending email, text messages, or even payment processing, authorization is in the critical path of each application request, which means that it must be satisfied in milliseconds, and must be 100% available to the calling application. This no longer sounds like Stripe: it sounds more like a library. And indeed, the only tools that developers rely on today to help with authorization are libraries.
Yet that’s not what developers mean when they want a turnkey authorization service. They want a central control plane that manages all the artifacts that are involved in making an authorization decision: the authorization policy, which is treated as code and stored and versioned in a git repository; user attributes, roles, and permissions, which are sourced from a set of identity providers; and context about resources that are used in authorization decisions. All of these must be available at the edge, where authorization decisions are made. Likewise, the decision logs that stream out of the authorization service should be aggregated and made available centrally.
We all want to have our cake and eat it too: a platform service that is managed and delivered by a vendor (control plane), but has the real-time characteristics of a library (edge service). Keeping these two parts of the architecture in sync is a distributed systems problem. And that’s why authorization is hard.
In the last few years, we’ve seen large technology companies such as Google, Netflix, and Airbnb, who run these systems as internal massive-scale distributed services, share their patterns more broadly. We can now use a set of open source building blocks, such as the Open Policy Agent (OPA), instead of having to come up with custom policy languages and engines. And a new set of solutions is emerging that could fill this glaring hole in the API economy.
The right architecture
When evaluating these new authorization services, we believe there are a few critical elements that must exist in order to satisfy the requirements for a scalable, available system. The edge service:
- Must run in the same subnet / kubernetes pod as your application, and therefore provide 100% availability: if your pod is up, the authorizer sidecar is up
- Requires NO connectivity with the outside world when making an authorization decision
- Makes an authorization decision in milliseconds
The control plane:
- Manages authorization policy in a central repository, and transparently synchronizes policy changes to the edge
- Normalizes user attributes, roles, and permissions from a set of identity providers and directories, and transparently synchronizes those to the edge
- Aggregates all of the decision logs from the edge nodes, to provide a single auditable system view of every authorization decision your application has ever made