This article explores ways you can make each tenant in your multi-tenant applications have different features – for instance, Visual Studio has three versions: Community, Pro, and Enterprise. This is often called versioning, and has four benefits:
- Having different versions / prices will increase your potential user base.
- The users that need the application’s advanced feature will pay more.
- Having different versions makes your really useful features stand out.
- People are more likely to try your service if there is a free version to try.
In this article I show how the features in the AuthPermissions.AspNetCore library (shortened to AuthP in these articles) allows to add versioning to your multi-tenant application. The other articles in “Building ASP.NET Core and EF Core multi-tenant apps” series are:
- The database: Using a DataKey to only show data for users in their tenant
- Administration: different ways to add and control tenants and users
- Versioning your app: Creating different versions to maximise your profits (this article)
- Hierarchical multi-tenant: Handling tenants that have sub-tenants
- Advanced techniques around ASP.NET Core Users and their claims
- Using sharding to build multi-tenant apps using EF Core and ASP.NET Core
- Three ways to securely add new users to an application using the AuthP library
- How to take an ASP.NET Core web site “Down for maintenance”
- Three ways to refresh the claims of a logged-in user
TL;DR; – Summary of this article
- The AuthP version 2 release allows you to create different versions of your multi-tenant application and you can now add extra Roles to tenant depending on what the customer selected and paid for – this it known as versioning.
- The AuthP version 2 release also a Role filter to hide Roles that allow access to advanced feature from tenant users, which means a tenant user can safely manage users in their tenant.
- The AuthP repo contains an ASP.NET Core MVC multi-tenant application which can be found in the Example3 project. This application is called the “Invoice Manage” and has three versions: Free, Pro, and Enterprise. This provides a runnable example of how to build a version a multi-tenant application using AuthP.
- The article covers
- How it hides advanced feature Roles from tenant users so that a tenant person can take over the job of managing the users in their tenant.
- The three types of Roles that let you to version different types of multi-tenant application.
- How to add the versioning Roles to your application.
- How to add Roles to a tenant to turn on different versions of tenants.
- How the admin user inside a tenant can get the correct roles for their tenant.
- How your admin staff can manually add Roles to a tenant.
Introduction to AuthP’s v2 improved Roles
In the AuthP library controls what a logged-in user can access via Roles and Permissions (read the original AuthP article to learn about Roles and Permissions). In version 1 of the AuthP library all the tenants could access the same set of Roles. This meant you a) couldn’t have different versions of a tenant and b) all the administration of AuthP users had to be done by top-level users of your multi-tenant application – referred to as app admin users.
The Part 2 article, which covers administration, I suggested that having an admin user in a tenant level (referred to as tenant admin users) is useful to reduce the work your admin staff has to do (I also think the tenants would like that as they can change things immediately). This is only possible because of an improvement in version 2 of the AuthP library which adding a RoleType to the Roles. The purpose of adding the RoleType was to:
- Allows a Role contains access to advanced features, such as deleting a tenant, to be hidden from tenant users. This allows a tenant admin user can manage their user’s Roles safely, as the advanced admin Roles aren’t available to a tenant admin user.
- Add two RoleTypes that are registered to a Tenant instead of directly to a AuthP user – these are known as Tenant Roles. This allows you to add extra Roles to a tenant, which gives the users in that tenant have extra features over the base (normal) Roles. This is the key to creating different versions of your application.
NOTE: There is also a default RoleType, known as a normal RoleType, which any user can have. These Roles define the basic Roles that everyone can use.
The next sections describe how these two changes helps when building multi-tenant applications.
- Hiding Roles that contains access to advanced features. This describes how a tenant admin can safely manage their users’ Roles, because Roles that a tenant user aren’t allowed to have are filtered out.
- Versioning your application. This describes how each tenant can have its own Roles, called tenant Roles. These tenant Roles allow you to add extra features in a tenant, thus creating the versioning of your application.
1. Hiding Roles that contains access to advanced features
The AuthP library controls access to your applications using features via Roles and Permissions, but some of the advanced features, such a deleting a tenant, should only accessible to your admin staff. The Permissions that control access to these powerful features are called advanced Permissions (see this section in the Permission document about advanced Permissions).
When a Role is created or updated the Permissions in the Role are scanned, and if any of the Permissions are marked as advanced Permissions, then the Role’s RoleType will be set to “HiddenFromTenants”. You can also set the RoleType manually (shown later in this article), but if advanced Permissions are found, then it will always set to “HiddenFromTenants”.
This change allows a tenant admin user to manage the Roles that their users have, as the Roles containing access to advanced (dangerous) features aren’t available to the tenant users. This feature is fundamental to allowing a tenant admin user to manage their users.
2. Versioning your application
As I said at the start, versioning your multi-tenant application can increase your potential user base and your profits. The multi-tenant application I designed for a client required different versions to cover the different features their retail customers needed, and of course the versions with more features had a higher price.
An analyse of the versioning needs comes up with three types of Roles to add versioning:
- Base Roles (also known as normal Role): These are Roles that every valid user can access, but a non-logged in user shouldn’t be able to access. This is the base version of your application.
- Tenant auto-add Roles: These are extra Roles in the higher versions of a tenant. If an auto-add Role is turned on in a tenant, then all logged-in users in that tenant can access the auto-add Role. In the example later I use an auto-add Role to provide extra analytics Role to the “Invoice Manager” application.
- Tenant admin-add Roles: These are extra Roles in the higher versions of a tenant. If an admin-add Role is turned on, its available for users in the tenant, but it’s not automatically added to every user in the tenant. This is useful in more complex applications where users within a tenant have different access. In the example later I use an admin-add Role to promote a user to be the tenant admin in the “Invoice Manager” application, which allows that user to invite new users to the tenant.
These three types of Roles allow you to build different versions into your multi-tenant applications. For SaaS (Software as a Service) applications your versions can offer customers different versions / prices for users to choose from. While a multi-tenant application for big multi-national company can not only separate the data for each division in a company, but also what features users in a division can access.
The next section describes an example multi-tenant application called the “Invoice Manager” which uses the three types of Roles defined to create an application with three versions.
Building the multi-tenant “Invoice Manager” application
The rest of the article show how I built a versioned multi-tenant application that I call the “Invoice Manager”. The Invoice Manager application is a multi-tenant ASP.NET Core MVC application and can be found in the Example3 project in the AuthP repo. It has three versions:
- Free: This version only allows one user in the tenant. This works for one-person companies, but also acts as a trial system that larger companies can try out the Invoice Manager’s features.
- Pro: This allows multiple users within the tenant, which works for companies with many employees. This uses a tenant admin-add Role to promote the person that signed up for the Pro (or Enterprise) version to a tenant admin, which allows that user to invite new user to join their tenant.
- Enterprise: This gives access to an invoice analyse feature to all the users in this tenant. This uses a tenant auto-add Role so that all the users in an Enterprise version tenant have access to the invoice analytical features. Companies that want this feature will pay extra to get this feature.
The Invoice Manager application allows a customer to sign up for this service where the customer picks what version they want / can afford. You have seen some of Invoice Manager features in the Part 2, administration article, but I didn’t cover how I managed the Roles and the versions. This article focuses on code / Roles that make the versioning work. The parts covered are:
- Adding the Role types needed for the three versions
- Creating a tenant with the Roles defined by the user’s selected version
- Allowing the tenant admin user to change a user’s Roles
- Manually adding Roles to a tenant
2a. Adding the Role types needed for the three versions
In this simple example I only need three Roles, one of each type.
- Tenant user: This Role allows a user to access the Invoice Manager’s features, e.g. listing and adding invoices. This holds the base set of Invoice Manager’s features and is a Normal RoleType, and every valid tenant user should have this Role.
- Tenant admin: This Role allows the user to a) sent invites to users to join their tenant, and b) can alter the Roles of users in the admin tenant’s tenant. This Role’s RoleType is admin-add, which it has be added to a user manually or by your sign-up code and is applied to Pro or Enterprise tenants, and not on the Free version.
- Enterprise: This Role unlocks an analytic feature it the Invoice Manager code. This Role’s RoleType is auto-add, which means the Role will automatically applied to every user within an Enterprise tenant.
The table below summaries the Roles in each version of the tenants.
TYPE | Free | Pro | Enterprise | What |
Normal | Tenant user (everyone has) | Tenant user (everyone has) | Tenant user (everyone has) | Gives access to basic features |
Admin-add | Tenant admin (given to a user) | Tenant admin (given to a user) | Can upgrade a user to Tenant admin | |
Auto-add | Enterprise (all users get this) | Gives every user in tenant to this feature(s) |
To create these Roles you need to be an app admin (see definition of an app admin and tenant admin in the part 2 article) and you manually set up each Role using AuthP’s AuthRoleAdminService’s CreateRoleToPermissionsAsync method. The screenshot below comes from the Example3 ASP.NET Core MVC application and allows you to manually create a Role and set the RoleType.

The tenant Roles (tenant auto-add and tenant admin-add) can be added to a tenant. The Normal Role can be added to any AuthP user, while the HiddenFromTenant RoleType can only be added to non-tenant users. The AuthP’s library checks these rules and will give you helpful errors if you try to assign a Role in the wrong place.
2b. Creating a tenant with the user’s selected version
UPDATE: AuthP 3.3.0 contains a generic service to set up a tenant, with versioning.
The original example was hand-coded and only worked for a single-tenant, no sharding design and using ASP.NET Core’s individual user accounts. But in the AuthP 3.3.0 release of the library I have created generic version of this feature such that a) its easier to use and b) it works with all types of tenants (single-level, hierarchical, non-sharding/sharding/hybrid) and works with a range of ASP.NET Core authentication handlers. This makes it much easier to add and configure this feature in your own applications. This feature is now referred to as “sign up /versioning” and the documentation can be found here.
Here is a screenshot of the “Sign up now!” page to remind you what the sign up page offers.

NOTE: Please do try this example. Clone the AuthP repo and run the Example3 ASP.NET Core project. On the home page there is a “Sign up now!” tab in the NavBar with will take you to the “Welcome to the Invoice Manager” page where you can select which version you want.
2c. Allowing the tenant admin user to change a user’s Roles
In the Pro and Enterprise versions the user that signed up to the Invoice Manager service it promoted to being a tenant admin. This allows this user to:
- Invite other users to join their tenant (shown in this section of the Part 2 article)
- List all the users in their tenant and change their Roles (shown in this section of the Part 2 article)
What the Part 2 article didn’t cover what is going on inside, especially when the tenant admin changes the Roles of a user in their tenant. In this case there are two types of Roles that the tenant admin can change:
- The normal (base) Roles that anyone can have
- The tenant admin-add Roles that are in the Tenant’s list of Roles.
NOTE: The tenant auto-add RoleTypes assigned to a tenant are automatically added to every logged-in user in that tenant.
When a tenant admin is editing a user’s Roles the page must list the normal and tenant admin-add Roles from the user’s Tenant. The AuthP user admin service contains a method called GetRoleNamesForUsersAsync(string userId) which looks for the two types of Roles to create a dropdown list of Roles that the user can have. This method takes the userId of the user being updated, so that it can add any admin-add Roles in the user’s tenant.
The screenshot below shows the user user1@4uInc.com who in the tenant called “4U Inc.”, who’s version is Enterprise. The dropdown list of Roles shows the normal Role called “Tenant User” and a tenant admin-add Role called “Tenant Admin” Role. Only the “Tenant User” is currently selected, but if the tenant admin selected the “Tenant Admin” Role too, then the user1@4uInc.com would also be an tenant admin too.

4. Manually adding Roles to a tenant
In the “creating a tenant with the user’s selected version” section the code added the correct auto-add or admin-add type Roles to a tenant when it is created. You may also add some code to allow the user to upgrade to a higher version too. But in some applications, you might want to simply leave it to an admin user to do this.
Only an app-level admin user (i.e. a user that isn’t linked to a tenant and has permission to use the add / update tenants feature. That’s because the Roles that a tenant has defines what Roles the tenant users can have, and you don’t want to allow a tenant user to bypass your versioning.
The screenshot below shows three tenants:
- 4U Inc. is an Enterprise version, with both the “Tenant Admin” (admin-add) Role and the “Enterprise” (auto-add) Role. (see the black tooltip with the two Role’s names when I hover the Tenant Role? column
- Big Rocks Inc. is a Free version, and as such doesn’t have any tenant Roles
- Pets Ltd. Is a Pro version, with only the “Tenant Admin” (admin-add) Role.

Conclusion
Many multi-tenant / SaaS applications use versioning, e.g. GitHub, Salesforce, Facebook, YouTube and even Twitter is now got a blue version (in some countries). Many applications offer free versions – GitHub for instance has a free version, but it has limitations which makes some companies pay for GitHub’s higher versions.
Versioning your multi-tenant application can will increase your potential user base and profits. The downside is it makes your application more complicated. The AuthP library will help, but it’s still takes careful work to create, market and manage different versions of your application.
The Invoice Manager application in the Example3 project in the AuthP repo is a great example to look at, and its designed to run using the localdb SQL Server that Visual Studio installs (or you can change the database connection string in the appsetting.json file if you want a different SQL Server). It automatically loads some demo data so that you can try various features written in the first three articles.
Happy coding.