Building multi-tenant web applications have many benefits over having a separate environment per each tenant. However, developing these applications needs a well-defined strategy for tenant isolation by design. Designing authentication and authorization plays a significant part in the tenant isolation strategy. Along the way, one of the critical decisions you need to make is either to use a standardized or a custom approach for authentication and authorization.

Considering the options at hand, I would recommend moving ahead with a standardized approach using OpenID Connect (OIDC) for authentication and authorization. Using an Identity framework like Identity Server reduces the effort in implementing OIDC for your applications. However, this comes with a few challenges where it requires to extend specific parts of the code in Identity Server to support multi-tenancy. Besides, the latest version v.4.0 of the Identity Server supports ASP.NET Core so far.

Tip: Easily build and distribute JS modules that everyone will love to use.

Bit helps you build and distribute JS functions so your team may use and develop them anywhere, to build faster together.

Pack, version and publish modules from any repo with zero overhead.

Easily find and consume modules as packages or as source-code.

Collaborate on individual modules right from any consuming project.

Keep your code DRY and consistent by syncing modules between microservices and between backend and frontend (auth functions, etc.)

Using OIDC for Web Applications

OpenID Connect is an authentication layer on top of OAuth2. When the user authenticates with the Identity Server, it issues JWT tokens called ID token and Access token. Identity Server will issue Refresh token as well depending on the OAuth2 Grant type. Each application (client) that registers with the Identity Server needs to request for the scopes required. Few example scopes include openid, profile, and email. Once the user gives the consent to access the particular scopes, Identity Server returns a set of user attributes called claims inside the ID token that are in connection with the scopes. These claims are typically used to verify a user and retrieve the necessary profile information for web applications. On the other hand, the Access token usually comes with a custom set of claims designed for the API to implement access control. When invoking an API from web application frontend (Or in a case of an API integration), we send the Access token with custom claims to the API in Authorization header for access control.

In the context of a web application, it is common to use Implicit flow for single-page applications. For server-side web applications, we use Code grant or Implicit flow based on the level of security required.

OIDC for Multi-Tenancy Implementation

When it comes to access control for multi-tenancy, one approach is to use the Identity Server only to issue the ID token and Access token, without any tenant-specific information. Then, from the web application backend side, it needs to retrieve the tenant id based on the specific user identifier passed as claims in the Access token. Another solution is to customize the identity server to include an additional tenant selection page soon after the user authenticates but before issuing the Access token. Then, we need to add the tenant-specific claims inside the Access token as required. Both approaches have pluses and minuses in comparison, and you could find different arguments recommending either of these.

In this article, I’m going to talk about the approach of customizing the Identity Server to have a tenant selection page and issuing tenant-specific claims inside the Access token. But can we customize the Identity Server to have an extra page for tenant selection? Can we delay the token issuing to happen after selecting the tenant? How can we put the claims relevant to the tenant inside the Access token? Let’s find out.

Customizing Identity Server

If you are using the Identity Server 4 version, creating a tenant selection page is quite straightforward. You need to have some knowledge of ASP.NET to implement a Server page for the tenant selection. Then it requires to intercept the internal navigation within the Identity Server to include this tenant selection page as apart of the authentication flow.

Implementing Internal Navigation for Tenant Selection Page

Implementing internal navigation could be achieved by extending the AuthorizeInteractionResponseGenerator Class and override ProcessInteractionAsync method to include the custom tenant selection page. Then you need to register this Class where you initialize the Identity Server as follows;

.AddIdentityServer(…).AddAuthorizeInteractionResponseGenerator<AuthorizeInteractionResponsHandler>();

Customizing the Claims for Tenant

Then it needs to include tenant selection page and later customize issued claims by creating a Class, implementing IProfileService Interface and GetProfileDataAsync method. Then you need to register this Class (Let’s assume its ProfileService) where you initialize the Identity Server as follows;

.AddIdentityServer(…).AddProfileService<ProfileService>()

Here, it is essential to know that the implementation of the Interface is invoked by the Identity Server twice to generate claims for ID token as well as for the Access token. You can identify the particular invocation by reading the ProfileDataRequestContext parameter passed inside GetProfileDataAsync method. For example, you can view the caller from context as follows;

context.Caller.Equals(“ClaimsProviderIdentityToken”)

When it comes to adding custom claims, it is also essential to keep the token size small by using shorthand names for scopes, claims, and values. Then in the backend API, it is possible to implement the Access token verification by using Identity Server extension and use policy-based authorize attributes for authorization at ASP.NET MVC Controller level.

Summary

Overall, using Identity Server for authentication and authorization solves many challenges in access control. But keep in mind that the approach I discussed in this article is only one way of implementing authentication and authorization for web applications using OIDC.

The main advantage of the Identity Server is that it is compatible with OIDC from the ground up. Using OIDC provides a standard approach for issuing claims and implementing a simple model for web application authentication and authorization.

However, if we try to use the Identity Server for multiple applications for Single Sign-On (SSO), this approach could be a bit challenging. Then you will need to implement custom strategies to issue Claims relevant to the scopes requested by each of the applications.

Learn More