Integrating Microsoft Entra ID into a Web App (Razor Pages)

.NET
Microsoft Entra ID

By Daniel Laufenberg on 08/05/2024

By Daniel Laufenberg on 08/05/2024

webapp_and_entra_id

Microsoft Entra ID (formerly known as Azure Active Directory) is one of the most popular Identity as a Service (IDaaS) products on the market today. If your organization uses Entra ID, it's likely that you've interacted with it before, even if you've never had to configure it yourself (larger companies usually have an IT department managing it).

Microsoft Entra ID is a cloud-based identity and access management service that provides all the tools your organization needs to manage user identities and protect its resources. The login screen you encounter before accessing your company’s internal app is an example of Entra ID ensuring that you are an authorized user. For more details, refer to the official documentation here.

Integrating Microsoft Entra ID into a Web App (Razor Pages) 4

At this point, you can probably imagine Entra ID just acting as a gatekeeper in front of your application—and you’re right. But what if I told you that instead of just serving as the front-line defense, you can integrate Entra ID directly into your application to provide fine-grained access control for each feature?

Let me show you how to accomplish this.  We'll create a web app (using Razor Pages) where each page has different access permissions based on the type of user. Before writing the application code, we'll need to create an application registration under our Entra ID tenant.

Key Concepts for This Demo

Service Principal

A service principal is an entity that acts as a security policy for your application. You can assign a service principal specific access permissions to resources within each registered Entra ID tenant. It's similar to defining an access policy for a user, but it applies to an application instead.

Application Registration

This is the process of creating a globally unique instance of an application object within the registered tenant. When you create a new instance of an application, you also create a service principal (of type “application”) tied to the application instance.

Wait... why do we need the application instance if we already have a service principal? 

Good question! Remember, application registrations are globally unique across all registered tenants, but service principals are not. This means that, in the case of a multi-tenant application, each tenant will need its own service principal.

Let’s Build It! 

We will create a sushi restaurant application with the following pages:

  • Home: Available to all users.

  • Kitchen: Available to employees only (users in the Entra tenant).

  • StockRoom: Available to the head chef only. We'll configure this with an “appRole”.

Prerequisites

Steps for app registration

1. Login to Azure CLI

az login 

2. Create an Application

#This creates the application registration 
az ad app create --display-name "sushi-restaurant" 
#Query the appId of the created registration here. 
az ad app list --display-name "sushi-restaurant" --query "[0].appId"  -o tsv

#Set the call back URL and allow token issuance, which are required for authentication. 
az ad app update --id {appId here}  --web-redirect-uris=https://localhost:7057/index
--enable-id-token-issuance

#Create and save the credential password here
az ad app credential reset --id {appId here}  --append --query password 

Step for configuring the web app

For the application code, simply download from this repository here. Most of the configurations are followed from this tutorial: https://learn.microsoft.com/en-us/entra/identity-platform/scenario-web-app-sign-user-app-configuration?tabs=aspnetcore

1. Open the project with a code editor of your choice, and add the following to appSetting.json

{
  "Logging": {
	"LogLevel": {
  	"Default": "Information",
  	"Microsoft.AspNetCore": "Warning"
	}
  },
  "AzureAd": {
	"Instance": "https://login.microsoftonline.com/",
	"TenantId": "{tenant Id here}",
	"ClientId": "{app Id here}",
	"CallbackPath": "/index",
	"ClientSecret": "secret you obtained from earlier"
  },
  "AllowedHosts": "*"
}

2. In program.cs, add the following configuration. This configures the application to use Entra ID for Authentication. 

builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration, "AzureAd");
builder.Services.AddRazorPages()
	.AddMvcOptions(options =>
{
	var policy = new AuthorizationPolicyBuilder()
              	.RequireAuthenticatedUser()
              	.Build();
	options.Filters.Add(new AuthorizeFilter(policy));
}).AddMicrosoftIdentityUI();

Notice we have 3 distinct pages in this web app.

Integrating Microsoft Entra ID into a Web App (Razor Pages) Image 2

Starting the Application

Let’s start the application. You can go into the terminal and run dotnet run.

The home page is available to anyone. The code for Index.cshtml.cs includes the [AllowAnonymous] attribute to make this page accessible to all users.

[AllowAnonymous]
public class IndexModel : PageModel
{
	private readonly ILogger<IndexModel> _logger;
…..
Integrating Microsoft Entra ID into a Web App (Razor Pages) Image 3

The kitchen page is protected by Entra ID. When accessing this page, the application checks for an existing auth session and, if none exists, redirects the user to the Azure AD login screen.

Integrating Microsoft Entra ID into a Web App (Razor Pages) 4

Now log in with your credentials (make sure to use an account under the tenant you registered your application with). 

Integrating Microsoft Entra ID into a Web App (Razor Pages) 7

Now lets access the StockRoom page, which is only accessible by the head chef. 

Integrating Microsoft Entra ID into a Web App (Razor Pages) 6

What happened here!?

If you look into the code for the in stockroom, there is a special Auth attribute role applied with “roles” restriction.

	[Authorize(Roles ="HeadChef")]
	public class StockRoomModel : PageModel
	{
…

The user you logged in with doesn't have this role assigned, so the application denied the request to view this page.

Let's create and assign this role to your user.

#Create an app role named "head chef"
az ad app update --id {your app Id here} --app-roles '[{ "allowedMemberTypes": [ "User" ], "description": "Only Head Chef has access to the stockroom.", "displayName": "Head Chef", "isEnabled": true, "value": "HeadChef" }]'
#Ensure that the new app role is created
az ad app show --id {your app id here} --query "appRoles[?value=='HeadChef'].id" -o tsv

One thing to note here is that we are creating an "appRole." It is beyond the scope of this article to explain the differences between app roles, Azure roles, and Entra ID roles, but for now, it's sufficient to know that this role is specific to application registration.

Now we need to assign this role to the user (make sure to assign it to the user you signed in with). This app role is not an Azure concept, so unfortunately, it cannot be done with the Azure CLI; it requires the Microsoft Graph API instead. Following this tutorial is the easiest way to accomplish this.

If you want more of a challenge, you can call the Graph API endpoint to do the same.

Now that your user has the correct role, let's navigate to the page (you will likely need to clear the session here).

Integrating Microsoft Entra ID into a Web App (Razor Pages) 7

Quick Recap

In this article, we integrated Microsoft Entra ID with a Razor Pages web application to implement fine-grained access control. We explained key concepts like service principles and application registrations, created a sushi restaurant app with different access-controlled pages, and configured Entra ID for authentication and authorization. Finally, we demonstrated role-based access control by creating and assigning the "Head Chef" role for restricted page access, ensuring a secure and controlled user experience.

Thanks for reading!

Related Posts


clouds-connected-RBAC

A Quick and Practical Guide to Microsoft’s RBAC Systems

Understanding Microsoft Role-Based Access Control (RBAC): Azure Roles, Entra ID Roles, Microsoft 365 Roles, and App Roles Explained with Examples.