How to gate end-user access to features: Shortcomings of plan identifiers, authorization & feature flags
Tags: api, programming, python, nextjs
Published: 2023-06-18 15:45:58

_This is part 2 out of a 3-part series about entitlements, first published on the Stigg blog, [here](https://www.stigg.io/blog-posts/entitlements-untangled-the-modern-way-to-software-monetization). If you're new to the concept of entitlements or haven't read part 1: "**Entitlements untangled. The modern way to software monetization**" yet, you can check it out [here](https://dev.to/getstigg/entitlements-untangled-the-modern-way-to-software-monetization-3fd6-temp-slug-2180622?preview=79f39159b6af843d3ac30edd4cf190b58cbeaff6cb338ade9ad93d6a16b4fda06f8f1fa29580ee3a3c69be3d1b09182ac26472b0ec944fb754bcc60b). Hope you enjoy reading it!_

Today, you might already use a number of concepts, which may seem like a proper solution to solve monetization challenges. Let’s discuss a few alternatives to entitlements, and the limitations and friction points you should be aware of. 

## Entitlements vs Plan identifiers
A common approach, especially in early-stage SaaS startups, is to tie access control to plan identifiers, that is, checking the user's subscription plan to determine access to specific features. 

For example, a structure like this can be found in the early days of a company:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5w6p2ukeqh6xh7lvkio8.png)

[Link to code](https://carbon.now.sh/?bg=rgba%2874%2C144%2C226%2C1%29&t=one-dark&wt=bw&l=application%2Ftypescript&width=554&ds=false&dsyoff=20px&dsblur=68px&wc=false&wa=false&pv=0px&ph=0px&ln=false&fl=1&fm=Fira+Code&fs=14px&lh=152%25&si=false&es=2x&wm=false&code=%252F%252F%2520list%2520of%2520available%2520plans%250Aenum%2520Plans%2520%257B%250A%2520%2520FREE%252C%250A%2520%2520PAID%252C%250A%2520%2520ENTERPRISE%250A%257D%250A%250A%252F%252F%2520customer%2520object%2520with%2520a%2520plan%2520identifier%250Aconst%2520customer%2520%253D%2520%257B%250A%2520%2520id%253A%2520%2522customer-123%2522%252C%250A%2520%2520currentPlan%253A%2520Plans.ENTERPRISE%250A%257D%250A%250Afunction%2520canAccessFeature%28customer%252C%2520minimumPlan%29%2520%257B%250A%2520%2520return%2520customer.currentPlan%2520%253E%253D%2520minimumPlan%253B%250A%257D%250A%250Aif%2520%28%21canAccessFeature%28customer%252C%2520Plans.PAID%29%29%257B%250A%2520%2520%252F%252F%2520no%2520access%250A%257D)


While this might seem like a straightforward solution, it has significant downsides that can lead to complexity and rigidity over time. 

**Brittleness of plan identifiers**
Plan identifiers can make your code brittle. The major drawback lies in the tight coupling it creates between your pricing model and the application codebase. Any change in the pricing plan, from changing the name of a plan, to changing the price or modifying the packaging, implies changes to the product behavior, which in turn necessitates updates to the code to accommodate it.

**Code maintenance**
Coupling the feature access logic with the pricing model will require changing the application code every time you want to adjust or change anything. That’s a lot of maintenance work you can save by decoupling plans and features. Your entitlement checks should only be concerned with whether a customer has access to a feature, not why they have access. This separation of concerns is vital for maintaining clean, maintainable, and flexible code.

**Changes and migrations**
Another aspect to consider is customer migration between plans. Say, for example, your business introduces a new plan or updates an existing one, and you wish to move customers to this plan. With plan identifiers, you would need to sift through the entire codebase to update the plan reference, even if you only want to make a change in the entitlement configuration. A task prone to errors and bugs.

**Custom settings for enterprise plans**
Enterprises often require custom settings that differ from standard plans, thus necessitating a unique plan. With plan identifiers, you'd need to create a new plan for each custom case, leading to a multitude of plans and identifiers that are difficult to manage and reason about.

**Custom code for usage and limits**
When you use plan identifiers, tracking feature usage or metering requires implementing usage limits in your codebase. This adds another layer of complexity, leading to a potential maintenance burden over time.


## Entitlements vs Authorization
Authorization is a method of restricting system access based on certain rules, such as role-based (RBAC), policy-based (PBAC), or attribute-based (ABAC) access controls, of individual users within an application.

While entitlements serve monetization, typically on account level, and authorization serve access control, typically on user level, it's possible to model entitlements using an authorization concept by mapping plans to roles, attributes or policies. Here is an example, the free and premium plans are mapped as roles, with each role having a set of entitlements modeled as permissions:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9yqgxae2rvbjv3zj631c.png)

[Link to code](https://carbon.now.sh/?bg=rgba%2874%2C144%2C226%2C1%29&t=one-dark&wt=bw&l=application%2Ftypescript&width=670&ds=false&dsyoff=20px&dsblur=68px&wc=false&wa=false&pv=0px&ph=0px&ln=false&fl=1&fm=Fira+Code&fs=14px&lh=152%25&si=false&es=2x&wm=false&code=const%2520roles%2520%253D%2520%257B%250A%2520%2520free%253A%2520%257B%250A%2520%2520%2520%2520permissions%253A%2520%255B%2522basic-dashboard%2522%252C%2520%2522reports%2522%255D%250A%2520%2520%257D%252C%250A%2520%2520premium%253A%2520%257B%250A%2520%2520%2520%2520permissions%253A%2520%255B%2522basic-dashboard%2522%252C%2520%2522advanced-dashboard%2522%252C%2520%2522reports%2522%255D%250A%2520%2520%257D%250A%257D%253B%250A%250A%252F%252F%2520assuming%2520the%2520user.permissions%2520is%2520derived%2520from%2520the%2520user%2520role%250Aif%2520%28%21user.permissions.includes%28%2522advanced-dashboard%2522%29%29%2520%257B%250A%2520%2520%252F%252F%2520no%2520access%250A%257D)


While this approach may work for less complex pricing models for a while, here are a few downsides you should be aware of.

**User access vs account access**
Authorization follows the principle of "least privilege," providing only the minimum access rights needed for a user to fulfill their role. Authorization isn't tied to the business model of a product, but rather to the organizational structure within which a product or system is used. For example, in a typical SaaS product, a user with the Admin role may have access to invite add or remove team members, while a user with the Member role may not.

**No support of usage limits**
Usage limits are not natively supported in authorization. While some advanced authorization policy engines can support this use-case, it's not a first-class citizen in most authorization implementations.

An example of how to implement entitlements using the Oso policy engine ([example from Oso's docs](https://www.osohq.com/docs/guides/model-your-apps-authz#entitlements)):

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3967tdzcmpsa9craehi0.png)

This approach will only work for monolithic applications, where the policy is evaluated against a single database. In distributed applications or microservices, where each service owns a subset of the policy’s data, accessing the usage can become even more challenging. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/po0pgqcqm3bvwzjpcxuj.png)

**Entitlements and JWT**
Some might consider storing customer entitlements on authentication tokens, like JWT, by writing custom claims or permissions as part of the token’s payload. Take into account that this method won’t be accurate enough for most monetization use cases. JWT tokens are stateless and are not typically updated until they're refreshed. 

This can be problematic for several reasons: 

1. Usage limits won’t be accurately updated. If a user purchased another seat - the entitlement limit should be increased immediately.
2. If you enrich the JWT payload with current usage, it won’t be accurately tracked. For example, a user exceeds their limit, but it won’t be reflected or blocked in time. 
3. If a user upgrades or downgrades to a different plan, they won’t have instant access to the new entitlements. 
4. If the company wants to grant a promotional entitlement to an account or change a user’s plan, those changes won’t be reflected in the user’s token immediately. 
5. This can lead to inaccurate or outdated permissions being enforced.


## Entitlements vs Feature Flags
Feature flags, also known as feature toggles, are a technique used in software development to turn certain features on or off at runtime, without needing to change or redeploy code. They are originally intended to be short lived and removed from the codebase, when not in use anymore. 

While both feature flags and entitlements provide control over software functionality, entitlements offer more sophisticated controls such as usage limits and tiered access levels that are automatically updated when a plan changes.

An example of an entitlement check can be performed using a LaunchDarkly SDK:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/elx5xngv6ew5an9mo18f.png)

[Link to code](https://carbon.now.sh/?bg=rgba%2874%2C144%2C226%2C1%29&t=one-dark&wt=bw&l=application%2Ftypescript&width=616&ds=false&dsyoff=20px&dsblur=68px&wc=false&wa=false&pv=0px&ph=0px&ln=false&fl=1&fm=Fira+Code&fs=14px&lh=152%25&si=false&es=2x&wm=false&code=const%2520canAccessFeature%2520%253D%2520await%2520ldClient.variation%28%250A%2520%2520%27premium-dashboard%27%252C%250A%2520%2520%257B%2520key%253A%2520user.id%252C%2520custom%253A%2520%257B%2520customerId%253A%2520user.tenantId%2520%257D%2520%257D%252C%250A%2520%2520false%250A%29%250A%250Aif%2520%28%21canAccessFeature%29%2520%257B%250A%2520%2520%252F%252F%2520no%2520access%250A%257D)


**The evolution of Permissioning Toggles**
Feature flags allow developers to turn features on or off, providing a way to test and release features gradually, perform A/B testing, and more. When considering entitlements in comparison, we could view them as an evolution of [Permissioning Toggles](https://martinfowler.com/articles/feature-toggles.html#CategoriesOfToggles:~:text=Operations%20person%20happy.-,Permissioning%20Toggles,-turning%20on%20new). However, they specifically cater to marketable features sold under various subscription packages, extending beyond the binary on/off use-case of traditional feature flags.

When compared to other types of toggles, the nature of entitlements is relatively high dynamism (i.e. when the toggle value changes) as they are tied to the pricing model and customer subscription level, and a longevity (i.e. how long the toggle exists in the codebase) that can span from at least a few months to a few years. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/byi39f4nrxgrzqjzmt9p.png)

You can read more about the different types of feature toggles in [this](https://martinfowler.com/articles/feature-toggles.html) excellent article by Pete Hodgson that was published on Martin Fowler’s website.

If you choose to use a feature flagging system to manage your entitlements, here are a few things you need to consider. 

**Deterministic changes in entitlements**
Feature flags are not part of the customer's contract and don’t have any financial impact. These flags can be used to test new features or variations with a subset of users, hence, they can be altered on the fly. Entitlements, on the other hand, are charged for and are bound by contract terms with a customer. Unlike feature flags that can be altered on the fly, entitlements change whenever a customer’s subscription or contract changes. Changes to entitlements usually require advance notice, and the current entitlements are typically only revoked after a sufficient “grace period” has expired. They are deterministic and should not be assigned ad-hoc, except in explicitly intended circumstances like for enterprise customers.

**‍Targeting rules**
The core of a feature flagging system is targeting rules among users (or customers), segments (or subscription tiers), and features. This could mean building custom automation workflows to keep everything synchronized when a plan or subscription changes. Entitlements, on the other hand, are natively tied to the customer subscription, automatically updating any changes.

**Ease of use**
Feature flagging tools are typically more technical and primarily targeted at engineers and product managers. This means Customer Success and Sales teams often find them difficult to navigate and understand, reducing their overall effectiveness for those teams.

**Again, custom code for usage and limits**
Feature flags don't inherently account for feature usage or metering. As a result, to track feature limits, you might need to define those limits into your codebase and automate toggling of flags when those limits are reached.


## Entitlements, Feature Flags, and Authorization: Stitching it all together
While it’s possible to duct-tape a solution to support your pricing model with one of the alternatives listed above, none of them will be sufficient enough on their own. Authorization, RBAC, and Entitlements each serve different purposes, are operated by different departments, and play a part in significantly different processes. To decide which tools you’ll need in your own stack, here’s an overview.  

**Feature Flags**: Primarily managed by Engineering and Product teams. They form part of the software delivery and product management process. A good use case for feature flags is, if you have a user that you want to include into the release of a beta feature and you’re not yet ready to roll it out across your entire user base.

**Entitlements**: Managed by Product, Sales, Marketing, and/or Growth teams. They form part of the pricing and packaging strategies, and customer contract management processes. A good use case is, if you have a feature you’re ready to monetize, and you want to roll it out to all existing and/or new customers.

**Permissions (Authz)**: Usually managed by the application administrators. They form part of the data security and compliance processes. A good use case is, if you want to enable customers to set different permissions for different users within a given account.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7b5bv3svw7dxncth9yf8.png)

A typical sequence of access checks in the code would start with a feature flag check at the user level. This determines whether the feature is enabled for this particular user or organization.

If the feature is enabled, the system then checks the entitlements at the organization level to confirm that the customer's subscription includes access to this feature.

Finally, the system checks the user's role-based permissions to ensure that the user is authorized to perform the requested operation within the feature.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/flpvs6fc2eoo45ui3gft.png)

If we’ll imagine this sequence in code within a React application, it will look similar to this:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o9pxqd24tt90mqlbebx3.png)

[Link to code](https://carbon.now.sh/?bg=rgba%2874%2C144%2C226%2C1%29&t=one-dark&wt=bw&l=application%2Ftypescript&width=554&ds=false&dsyoff=20px&dsblur=68px&wc=false&wa=false&pv=0px&ph=0px&ln=false&fl=1&fm=Fira+Code&fs=14px&lh=152%25&si=false&es=4x&wm=false&code=%252F%252F%2520check%2520if%2520the%2520user%2520is%2520logged%2520in%250Aif%2520%28%21isAuthenticated%28%29%29%2520%257B%250A%2520%2520return%2520%253CRedirect%2520to%253D%2522%252Flogin%2522%252F%253E%253B%250A%257D%250A%250A%252F%252F%2520check%2520if%2520the%2520user%2520can%2520see%2520the%2520new%2520members%2520page%250Aif%2520%28%21isFeatureFlagEnabled%28user%252C%2520%2522members-page-v2%2522%29%29%2520%257B%250A%2520%2520return%2520%253CRedirect%2520to%253D%2522%252Flegacy-members-page%2522%253E%253B%250A%257D%250A%250A%252F%252F%2520check%2520if%2520the%2520organization%2520is%2520paying%2520for%2520the%2520teams%2520feature%250Aif%2520%28%21isEntitledTo%28user.orgId%252C%2520%2522feature-teams%2522%29%29%2520%257B%250A%2520%2520return%2520%253CRedirect%2520to%253D%2522%252Fpricing%2522%253E%253B%250A%257D%250A%250A%252F%252F%2520check%2520if%2520the%2520user%2520has%2520permission%2520to%2520view%2520team%2520members%250Aif%2520%28%21hasPermission%28user%252C%2520%2522members%253Aview%2522%29%29%2520%257B%250A%2520%2520return%2520%253CRedirect%2520to%253D%2522%252Frequest-admin-access%2522%252F%253E%253B%250A%257D)

**Scatter/Gather**
To stitch these three systems together and make sense of them, one practical approach is to create a centralized place for access checks, such as an API gateway. This gateway can take on the responsibility of authorizing operations using a "scatter-gather" pattern on the backend.
It "scatters" the authorization checks across the feature flag, entitlement, and permission systems, then "gathers" the results to make an overall decision. It can also pass this context on the request to downstream services so they won't have to fetch this data again in case they need it for their internal logic.

This approach encapsulates the complexity of the authorization checks and provides a unified interface to the rest of the application. It also allows for easier updates and changes as the underlying systems evolve.

An example of a “stitched” user data including feature flags, permissions and entitlements:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/05klkqdl5vzm3d49sd9h.png)

[Link to code](https://carbon.now.sh/?bg=rgba%2874%2C144%2C226%2C1%29&t=one-dark&wt=bw&l=application%2Ftypescript&width=554&ds=false&dsyoff=20px&dsblur=68px&wc=false&wa=false&pv=0px&ph=0px&ln=false&fl=1&fm=Fira+Code&fs=14px&lh=152%25&si=false&es=4x&wm=false&code=const%2520user%2520%253D%2520%257B%250A%2520%2520featureFlags%253A%2520%255B%27dashboards-early-access-2023%27%255D%252C%250A%2520%2520permissions%253A%2520%255B%250A%2520%2520%2520%2520%27boards.view%27%252C%250A%2520%2520%2520%2520%27boards.open%27%252C%250A%2520%2520%2520%2520%27boards.archive%27%252C%250A%2520%2520%2520%2520%27attachments.upload%27%252C%250A%2520%2520%255D%252C%250A%2520%2520currentTenant%253A%2520%257B%250A%2520%2520%2520%2520tenantId%253A%2520%27d74a2ae630b6%27%252C%250A%2520%2520%2520%2520name%253A%2520%27Acme%2520Inc%27%252C%250A%2520%2520%2520%2520entitlements%253A%2520%257B%250A%2520%2520%2520%2520%2520%2520%27advanced-dashboards%27%253A%2520%257B%257D%252C%250A%2520%2520%2520%2520%2520%2520%27data-retention-days%27%253A%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520value%253A%252014%252C%250A%2520%2520%2520%2520%2520%2520%257D%252C%250A%2520%2520%2520%2520%2520%2520%27file-storage-mb%27%253A%2520%257B%250A%2520%2520%2520%2520%2520%2520%2520%2520usage%253A%2520128%252C%250A%2520%2520%2520%2520%2520%2520%2520%2520limit%253A%25201024%252C%250A%2520%2520%2520%2520%2520%2520%257D%252C%250A%2520%2520%2520%2520%257D%252C%250A%2520%2520%257D%252C%250A%257D%253B)


## In Part 3 of this technical deep dive
...we're sharing a curated list of best practices when working with entitlements. We've made it our company's mission to solve this complex issue for engineering teams and have learned a lot building Stigg. Whether you decide to look for a solution or build a service yourself, this list will help you make informed decisions. Check out our best practices [here](https://dev.to/getstigg/best-practices-every-developer-should-know-when-working-with-entitlements-3m9c-temp-slug-6885361?preview=1f335e3b2ed59fd5c0c0a9cc36c5d10b957185a10ca9ff6b6ad6371604f5543080581d55ca6047311659670caa5f54e5310104cd6d2a461f75842e87).


## To wrap up
Operating in highly dynamic markets, SaaS companies have much less chances of success when compromising on  rigid pricing models  managed in the codebase. 

Entitlements are the base for a truly flexible taxonomy and speed in go to market, allowing developers to quickly build and update pricing and giving GTM teams the opportunity to experiment with pricing & packaging, without code.

We made it our mission at [Stigg](https://www.stigg.io) to solve entitlements for SaaS. Our API gives you a flexible and scalable infrastructure, including metering, built-in experiments, a bunch of snap-in widgets, and native billing integrations, out of the box, and provides the automation to orchestrate everything about your pricing & packaging. If you consider buying over building, check out our [Free](http://app.stigg.io/signup) plan. You can use one of our sandboxes to see all functionalities of Stigg, even before integrating it.