5 Best Practices for Cloud-Native Permissions

Cloud-native/microservices-based products are complex, and so is building access control and managing permissions for these products. And it’s only getting worse with each pull request. Most developers end up building authorization or access control for their products multiple times—forced to refactor with each new customer, product or security demand. To make our lives a little easier, let’s look at the unique challenges that building cloud-native permissions poses and cover the five best practices for building them that can save you a lot of hassle.

Applications and Access Have Changed

We used to build authorization by using monolithic frameworks like Django or Spring that came with authorization or access control baked-in, but these are no longer applicable when we create cloud-native applications. 

There are a few reasons for that. First, applications themselves are no longer monoliths—they’re based on microservices and are becoming highly distributed. This becomes even more critical when you need to incorporate devices or instances that are deployed at the edge and which often need access control, too. 

Second, cloud-native applications tend to require integration of third-party services (such as billing, authentication, databases, analytics, etc.) and the ability to control access to them in addition to your own application’s microservices. 

Third, more dynamic and distributed applications require us to use a bunch of different authorization models (e.g. RBAC, ReBAC, ABAC) that are based on multiple data sources and increasingly complex rules. Finally, security, privacy and compliance demands are also rising (in the face of increasingly complex cyber threats) and becoming really complex. We find ourselves not only managing who should access the data but also how it is propagated between different services.

The Reality of Modern Authorization

All of these new needs require us to adopt a different mindset when thinking about authorization:

  • Authorization can no longer come as an afterthought; we have to plan it in advance.
  • Authorization is an ongoing endeavor, not something you solve once. It has to keep evolving along with your product.
  • Authorization is key in a customer’s experience, as it affects how users connect and invite others to the product. They won’t like it if it’s bad.
  • Authorization is connected to the bigger space of identity and access management (IAM)

The Five Access Best Practices

To handle all of these changes, there are some best practices that will aid you in building cloud-native permissions and leave you time to actually develop features, rather instead of just running around handling permissions all day.

1. Decouple policy and code

One of the most important practices in building cloud-native permissions is decoupling of policy and code. Having the code of the authorization layer mixed in with the application code itself can be very problematic. Most importantly, it creates a situation where we struggle to upgrade, add capabilities and monitor the code overall as it is replicated between different microservices. Each change would require us to refactor large areas of code that only drift further from one another as these microservices develop. 

This can be avoided by decoupling our policy from our code by (ideally) creating a separate microservice for authorization that will be used by the other services to fulfill their authorization needs. Open source policy/permissions engines such as OpenPolicyAgent (OPA) or SpiceDB, for example, allow us to manage authorization in a separate service.

2. Be event-driven

We want the application that we are building to be dynamic. Applications often include abilities such as user invites, role assignment or the use of third-party data sources—all of which should be managed in real-time. Without this capability, our ability to make authorization decisions will be significantly reduced. 

This requires us to design our authorization layer to be event-driven. We want to create a reality where every time an event that affects authorization happens, it is immediately put through the system to make sure the authorization layer learns about it and remains in sync with the application and any relevant third-party data service. 

Ideally, to achieve this, we’d decouple the authorization data from the application data (as not all the data that is relevant for the application is relevant for authorization and vice versa), creating a lean model in our authorization layer and then keeping it in sync with our application and additional sources through real-time events. 

Open Policy Administration Layer (OPAL), for example, is an open source project that enables making OPA event-driven. This allows you to respond to policy and data changes, push live updates to your agents and bring open policy up to the speed needed by live applications.

3. Backoffice integration for stakeholders

The authorization layer is part of the product itself and in product-focused companies, there are various stakeholders that need to be able to connect to the access control experience. Along with developers, these include DevOps, product managers, security, compliance, sales, marketing, etc. While building the authorization layer, we want to provide controls and interfaces to these various stakeholders through a back office system. This requires us to consider what different stakeholders would need from the access control interface to our product from day one. That should keep everyone happy.

4. Interfaces for customers

Similar to the way we consider stakeholder requirements, we also need to think of our end users/customers. Authorization is not only relevant for managing the product, but also for the product’s end user. If, for example, users require access to their own audit logs (something almost every B2B application user would need), they should easily be able to see what they have done within the product. Recognizing this need in advance calls for building the authorization layer in a way that enables it to latch on to different interfaces that cater to the needs of the end users. 

5. GitOps

So we’ve created a separate microservice to manage permissions and we are able to deliver updates to it in an event-driven fashion. Now, how do we manage those changes, apply versions, apply various checks and balances and make sure that the code and data for the microservices complies with our demands and requirements? 

The answer is GitOps. 

Using GitOps allows us to create a pull request for every version change. Then, as our developers are updating the product and its access control functionality, they can push a new commit with new code, have those go through the necessary tests and checks and apply them to the authorization layer.

The Future of Permissions

With complexity on the rise and a constant stream of customer and security demands, building your product’s access control in a way that will be ready for the future and won’t require heavy refactors or rewrites is critical. Creating a separate microservice for authorization, designing it to be event-driven, providing controls and interfaces to various stakeholders and customers and using GitOps allows us to create a product that is as future-proof as possible and prevents us from having to rebuild our authorization layer over and over (and over) again—no matter the requirements.

Or Weis

Or is the CEO and co-founder of Permit.io, and co-maintainer and author of open source OPAL.ac. Or is a serial entrepreneur who is passionate about developer tools, previously founding Rookout.com, a leading production debugging solution; and managing Upwards Israel’s largest founders’ PLG community.

Or Weis has 1 posts and counting. See all posts by Or Weis