Have you ever chatted with a fellow developer about an application’s permission system and quickly realized you’re also talking about its login system? It’s rather unfortunate, but these two entirely distinct systems often get merged together simply because their formal names start with the same four letters: AUTH.
Authentication (“authN” or “identity”) is who you are
Authorization (“authZ” or “permissions” or “access control”) is what you’re allowed to do
This is no amateur mistake. Even major web frameworks bundle these concepts together out of convenience.
Because so many applications need to support users from inception, identity becomes vital for developers to understand on day one. However, building a robust permission system can usually be deferred until users start demanding it. When requests for fine grained access control inevitably start pouring in, they often come alongside feature requests for integrations with various Identity Providers. This makes it seem natural to assume that the permission systems should be direct integrations with the primitives that the Identity Providers expose. However, the authorization functionality that is often found in most authentication systems is generally overly simplistic and the resulting permission systems that are built on top are usually fragile and error prone.
This is the last thing you want to hear when discussing software that determines whether or not a user has access to sensitive content. If you’re thinking “that’s only if you work in a domain, like healthcare or government, where you know sophisticated access control is required”, you should consider that even in simple use cases you’ll likely be iterating on your design, which gives you ample opportunities to introduce bugs that manifest themselves as security vulnerabilities.
Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization’s communication structure.
— Melvin E. Conway
Conway’s Law describes how the architecture of software is a reflection of the organization of the people that built it. For example, software components that are decoupled, but belong to the same application are often separate only because they were built by separate teams.
When permission systems are based on Identity Providers, this law is entirely reversed.
If an organization of people cannot use any software because everything, for example, only supports a structure that can be modeled by LDAP Groups, it forces the people to reorganize into something that can be modeled by LDAP Groups.
This may be viable for organizations like businesses (the reorgs will continue until morale improves!), but obviously not all software can demand that their users reorganize just to use their product.
LDAP Groups, OAuth Scopes, SAML Claims, JWT Claims: all a rose by any other name. These concepts all represent the same kind of data: an attribute that is stored on a user, indicating something about that user. Attributes are useful: they can provide context about a user (such as an object they can access, or a role that the user has), which makes such a system, in theory, a reasonable solution to determining what a user can access. However, in practice, developers realize that this isn’t quite so obvious once they have started working with this data.
Software that relies on these concepts for permissions all struggle in the same core principle: how they choose to interpret and apply significance to the presence of an attribute:
You can see how this quickly gets out of hand. And once it’s been decided how attributes should be properly interpreted, it’s time to audit every other application and make sure they interpret the attribute the exact same way or else you might have a security problem!
Now, there is nothing fundamentally wrong with attribute-based permission systems. In fact, mature permission systems are almost always a fusion of ideas from various models based on the requirements at hand. In this case, how the attributes from authentication systems manifest themselves when they become the foundation of a permission system is the problem. This is because attributes can only state facts about an identity. But what we really need is the answer to the question “can this subject take this action on this resource?”.
While identity is required to ask the question “Does this
subject have permission to do this
action to this
object?”, it is not the only variable.
Identity sits alongside the action and the object.
That’s all well and good, but how should one arrive at the answer to one of these questions?
A great place to start is to crack open a social network like Facebook. Go review (and probably update) your privacy settings; you’ll find a variety of configurations for sharing your content like friends-only or friends-of-friends. When you change that setting, you’ll find that it applies instantaneously; there is no migration happening in their backend where thousands of users are granted the attribute to view your content. This is because Facebook is designed to store and query relationships between their users. Facebook is powered by a social graph.
If we lean on the idea of modeling relationships, like Facebook does, you can change the question from “Does X have permission to do Y to Z?” into “Does X have the relationship Y with Z?”. For example, “Does User #123 have the Write relationship with Document #456?”. Recall the example from reading attributes where the application has to decide if the “admin” attribute also implies the “write” attribute. In a relationship-based model, this problem disappears because these are just more relationships:
The beauty of a relationship-based system is that the application doesn’t care how the user got to the Write relationship. Maybe they’re the user that created the document. Maybe the user had some kind of admin relationship that gives them the ability to do everything. It doesn’t matter as long as there is some path through our relationship graph from the user to the Write relationship on the document.
This means that relationships can change (like changing your privacy settings) and applications will not need to have their code rewritten because there was no longer anything left open to interpretation.
By realizing that permissions systems are fundamentally coupled to the relationships between people and objects in our software, we can build systems that mimic the way people naturally organize their world to be most effective. This not only empowers the people consuming the software, but leads developers to arrive at more robust permission systems that can withstand changes to the organization.
If you’re left wondering what a permission system based on relationships looks like in practice, Authzed is exactly that! We’re currently working hands-on with customers to help them understand and migrate to a better permission system. If adding or refactoring permissions to support new functionality in your app is next on your roadmap, reach out to us.