Quote of the Day

more Quotes

Categories

Buy me a coffee

  • Home>
  • Azure>

Building multitenant application – Part 3: Authentication

Published August 20, 2022 in Azure , Azure Active Directory - 0 Comments

In this post, I continue to share what I have learned while building a multitenant application by ultilizing Microsoft Identity Framework and SQL role level security. Specifically, I share what I have learned following Microsoft example project and documentations to make authentication works in a multitenant environment.

First, it’s helpful to distinguish between service principal vs application context in the context of multitenancy.

Service Principal vs Application Object

An application object is a unique identifier representing the instance of the application in a tenant which hosts the application (the application’s home tenant).

A service principal is created in every tenant where the app is used. The application object acts as a blueprint to create service principals.

Application Registration

Making the app multitenant means allowing users from multiple Azure AD tenants to authenticate. For this to work, when registering an application in azure, you must choose either “Accounts in any organizational directory”, or “Accounts in any organizational directory and personal Microsoft accounts”.

As a side note, personal account means a Microsoft or Xbox account, and not just any account. For instance, a user will not be able to login with a Gmail account. One way to allow a user with another account type such as Gmail or Yahoo is to add the user as a guest account in your tenant. However, note that adding guest accounts to your tenant may incur additional fees (For more info, see MAU billing model for Azure AD External Identities – Microsoft Entra | Microsoft Docs).

My app follows a typical setup which includes both a single-page web app and a web API. The web app obtains an access token from azure AD to access the web API on behalf of the user, via an authorization code grant with PKCE . In azure AD, I register both applications. Additionally, I configure Expose an API section for the web API, and API permissions for the web app.

In the screenshot below, I define a scope for the web API in the Expose an API section.

Add the scope defined for the web API in the API Permissions section of the web app.

Below shows how I add the scope to the list of API permissions for the web app. Notice the type of permission is Delegated.

Configure permissions for web app to access web API

Provisioning applications

Before a user from another tenant can login into the web app, the user’s tenant must aware of both the web app and web API. This entails provisioning both apps in the user’s tenant. The end result of a successful app provisioning is that both apps appear under Enterprise applications in the user’s home tenant.

Provisioning just the web application is simple. When redirecting to the authentication endpoint, you need to set the prompt parameter to “admin_consent”, as shown in the below snippets, which I take from the multitenant-saas-guidance sample project.

 /// <summary>
        /// Called prior to the OIDC middleware redirecting to the authentication endpoint.  In the event we are signing up a tenant, we need to
        /// put the "admin_consent" value for the prompt query string parameter.  AAD uses this to show the admin consent flow.
        /// </summary>
        /// <param name="context">The <see cref="RedirectContext"/> for this event.</param>
        /// <returns>A completed <see cref="Task"/></returns>
        public override Task RedirectToIdentityProvider(RedirectContext context)
        {
            if (context.Properties.IsSigningUp())
            {
                context.ProtocolMessage.Prompt = "admin_consent";
            }

            _logger.RedirectToIdentityProvider();
            return Task.FromResult(0);
        }

In the sample project, when the user clicks the button to register the tenant, azure ad displays the admin consent flow. After the user has consent, azure creates the service principal for the web app in the user’s tenant.

Things get more complicated when it come to provisioning the web API. This is because you don’t typically provide an interface or URL to redirect the user to the admin consent flow. From what I understand, one way to tell azure to provision both the web app and API is to add the client id of the web application to the “knownClientApplication” list of the web API, as shown in below screenshot.

Adding client id of web app to the knownClientApplications list of web API.

If things work correctly, the admin consent flow screen should include the permission defined in the Expose an API section of the web API, and when the user has consent, azure should create service principals for both the web app and API in the user’s tenant. However, I could not get the provisioning to work by following the instructions.

The only way I was able to get provisioning to work correctly was using a PowerShell script, as shown in the below snippet.

$tenantId="ce5bb1c9-6a62-41fe-bd7c-724568db301c"
if ((Get-Module -ListAvailable -Name "AzureAD") -eq $null)
{
    Install-Module "AzureAD" -Scope CurrentUser
}
$webApiAppId="e0371164-059d-48c7-8091-4ec4c3bd718e"
$webAppId="13ff7063-5c61-40f5-80e0-31285eecba2f"

Connect-AzureAD -TenantID $tenantId

 #First provision the service principal of the web API 
New-AzureADServicePrincipal -AppId $webApiAppId

# Then, provision the service principal of the multi-tenant web app 
# that requests token for the web API. 
New-AzureADServicePrincipal -AppId $webAppId

Note that you need to install Azure Active Directory PowerShell module.

In the above snippets:

  • tenantId: Replace with the id of the target tenant to where you want to provision the app. For example, I use the id of the test tenant I created.
  • webApiAppId: Replace with the client id of your web API, as registered in the source tenant.
  • webAppId: Replace with the client id of your web app, as registered in the source tenant.

After running the script, both apps show under Enterprise Applications in the target tenant, as expected.

Service principals created in the target tenant

Note that the search has some default filters enabled. Initially, I did not see the apps at first, and thought I did something wrong; however, after removing the filters, they show up.

In summary, below list the high level tasks I have gone through to make my apps multitenant:

  • Update app registrations to allow users from any organization to authenticate.
  • Use PowerShell scripting to provision both the web app and web API in each of the target tenants from which I want to allow users to authenticate and access my apps.
  • Setup row level security to secure the data at the database layer (See this post).
  • From the web API, calls the database procedure to set the id of the authenticated user in the database application context (See this post).

References

GitHub – mspnp/multitenant-saas-guidance: a guidance project for implementing multi-tenant SaaS web applications on Azure

Build apps that sign in Azure AD users – Microsoft Entra | Microsoft Docs

Architecting multitenant solutions on Azure – Azure Architecture Center | Microsoft Docs

Multitenancy and identity management – Azure Architecture Center | Microsoft Docs

Azure AD Multi-tenant Apps: API Chains and Cyclic Dependencies – Joonas W’s blog

What is provisioning with Azure Active Directory? – Microsoft Entra | Microsoft Docs

Apps & service principals in Azure AD

Install AzureAD PowerShell for Graph | Microsoft Docs

No comments yet