Fixed it: Authorizing at the edge.

image

Cloudentity is a dynamic authorization company that enables customers to deliver rich authorization decisions at the edge. But what does that actually mean? What edge are we talking about, and does it really even exist? Yes, and yes. Let's dive into it.

Four score and seven years ago, we built applications containing every piece of business logic you could imagine. The term we use for this is monolithic. Whether it was verifying user input, calculating mathematical functions, or applying business policy, it was all housed in one application.

If it was really bad, one package of classes, but I digress. The point is everything we needed to do was tightly coupled in one executable. So this means if we needed to update the business policy code, the entire application needed to be updated. However, the good thing about this architecture was that we could centralize certain functionality, like authentication or authorization. Our application's other functions didn't have to worry about having that logic because they could call into other parts of the application. Fast forward to today, and we've completely separated from that philosophy. We now break things up into small specialized pieces of code (called services) that handle as few things as possible (we call that a microservice), and we build them to be independent of other each other, so that way if one needs to be updated, it doesn't affect the others.

This gets us away from our monolithic architectures of old and allows us to build at the speed and scale we see today. Microservices can be deployed and updated at will while still allowing the application to deliver functionality. However, the one advantage we lost with this is the centralized functionality. To make sure we had complete independence, some functionality would have to be rebuilt into each microservice. On a small scale, this isn't that bad. A little inefficient, and possibly issues with duplication of code, but manageable. However, at scale, it starts to become an issue. Specifically, I'm talking about authentication (AuthN) and authorization (AuthZ). As a microservice application grows and starts to handle more functionality, the need for both authN and authZ grows with it, placing a burden on each microservice to handle the job. Add into the mix the different types of AuthN and AuthZ protocols that can be used. You can find yourself in a situation where you need a centralized way to handle authN and authZ functions but deliverable to all your microservices.

Let's solve this problem, shall we? So the first thing we need is a way to centralize the authentication point for our services. We need to make sure that an entry point can handle various authentication protocols and combine them into an easily consumable object that our backend services can use. So no matter if they are using SAML, OIDC, or any other combination of letters, we can handle the authentication and create an object that validates the user. Additionally, we need to normalize that object's attributes so that way our backend services don't have to do any guesswork.

OK, looking good so far. Now that we have authenticated the user, we need to make sure that they are authorized to do whatever they are trying to do. We will follow the same principles we did for our authentication service and make sure our authorization service is centralized and provides a consumable object for our backend services. OAuth makes the most sense here, as its entire job is authorizing. We can offer our backend services a standard way of receiving authorization by receiving the Oauth access token and the scopes for that token. Again, we want to make sure we normalize the types of scopes that we are creating here. So it will look something like this:

OK, so we've taken the burden away from our backend services of handling authN and authZ (authNZ), but we arent' done yet. We still need to make sure that our services get access to the authNZ service we just built. We could say that our backend services will always call out our authNZ services whenever they need that functionality. But remember, we're talking about scale here, so every microsecond counts. We don't want to have hundreds of microservices all calling and depending on one service. That's like having the best pizza in the city but offering no delivery service. Let's not be that place. Instead, let's deliver the functionality right to our backend services, and going a step further, we won't even need the services to make a call out; we'll handle the functionality for them and pass them our authNZ object once it's done. That's better than delivery service, that's bringing to your kitchen and placing it right on your plate. Anybody else hungry? Moving on.

All right, so we've built out our architecture to solve this problem. We've centralized authNZ, normalized the attributes that allow decisions, created an easy to consume object for our backend services and delivered that object right to them while doing the dirty work of handling the authNZ logic. We can scale, and we can be secure. Ship it.

So what does this actually look like in the real world? I'm so glad you asked. Let's take a look at a real-world example:

Netflix was having an issue similar to what I've laid out so far. In their words:

To summarize, we found ourselves with a complex and inefficient solution for handling authentication and identity tokens at a massive scale. We had multiple types and sources of identity tokens, each requiring special handling, the logic for which was replicated in various systems. Critical identity data was being propagated throughout the server ecosystem in an inconsistent fashion.

So they had massive scale, multiple types of authentication tokens, unique logic for each token, and replication of that logic throughout their services. Sounds familiar, right? I encourage you to check out their blog for how they fixed their problem.

Now Netflix built its solution from the ground up, but as you can see from the architecture diagrams, it looks a lot like what we've put together in this blog. This is just one example of how authN and authZ are architected in the modern world and how Cloudentity can help you solve these challenges without building them yourself. You don't have to be Netflix to have a good solution; you can be your company and use Cloudentity. Speaking of Netflix, if you all ever want to have a chat about what you've built, we'd be happy to have a discussion.

Happy streaming y'all.