Introduction:
Secure authentication and authorization mechanisms are essential in modern application development. Microsoft Entra ID (formerly Azure AD) provides a powerful and scalable identity platform that integrates seamlessly with .NET. In this article, we’ll walk through building a secure .NET 8 Web API and a Blazor App that authenticates and calls the API using Microsoft Entra ID.
We’ll be creating:
- .NET 8 Web API – Protected using Microsoft Entra ID for authorization.
- Blazor App – Authenticates users and accesses the protected API securely using access tokens.
Prerequisites:
- .NET 8 SDK
- A Microsoft Entra ID tenant
Microsoft Entra ID App Registrations:
Web API App Registration
Register a new app in Entra ID:
- Name: Provide a name for your application. I named it Azure-MS-EntraID-Management – API
- Platform: None (for now)
- Supported account type – I went with Accounts in this organizational directory only (Default Directory only – Single tenant). Based on your requirement, switch between the different account type options
- Select Expose an API from the Manage blade, click on Add a scope api://<client-id>/UserRole.Read as shown in the figure below

Save the Application (client) ID and Directory (tenant) ID
Blazor App Registration
Register a second app:
- Name: Provide a name for your application. I named it MS-EntraID-Workflow
- Platform: Web
- Redirect URI: Add the redirect url, In my case it is https://localhost:7173/signin-oidc
signin-oidc is the callback endpoint used for processing the login response from Microsoft Entra ID. It’s part of the ASP.NET Core OIDC authentication flow.
Authentication settings:
- Check ID tokens and Access tokens
- Add api://<Azure-MS-EntraID-Management – API>/UserRole.Read to API permissions
- Grant admin consent, as shown in the figure below

Build the .NET 8 Web API
Create a .NET 8 Web API project with Visual studio or VS Code
Add Microsoft Identity Packages
Microsoft.Identity.Web
Microsoft.AspNetCore.Authentication.JwtBearer
Configure appsettings.json
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "<Your-MS EntraID Tenant ID >",
"ClientId": "<Your Registere Application Client ID>",
"Scopes": "UserRole.Read"
}, Program.cs
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddAuthorization(config =>
{
config.AddPolicy("AuthZPolicy", policyBuilder =>
policyBuilder.Requirements.Add(new ScopeAuthorizationRequirement() { RequiredScopesConfigurationKey = $"AzureAd:Scopes" }));
});
The above statements will register the OpenId Authentication Authorization policies
And use app.UseAuthorization() middleware
Create WeatherForecastController.cs and add the below Action
[ApiController]
[Route("[controller]")]
[Authorize(Policy = "AuthZPolicy")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
The AuthZPolicy will protect the GetWeatherForecast action, which will only allow requests with the UserRole.Read scope.
Create the Blazor Server Application
Create a Project using Visual Studio or VS Code
Add the following Microsoft Identity Packages
- dotnet add package Microsoft.Identity.Web
- dotnet add package Microsoft.Identity.Web.UI
- dotnet add package Microsoft.Identity.Web.TokenAcquisition
Configure appsettings.json
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "<Your domain>",
"TenantId": "<Your Tenant Id>",
"ClientId": "<Your MS Entra ID application client ID>",
"CallbackPath": "/signin-oidc"
},
"DownstreamApi": {
"Scopes": [ "api://<Your MS Entra ID API App Client ID>/UserRole.Read" ],
"BaseUrl": "https://localhost:7100/",
"RelativePath": "WeatherForecast"
Use Secret.json service to configure the client secret
"AzureAd":{"ClientSecret": "<Your MS Entra ID application client ID>"}Program.cs
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(configureMicrosoftIdentityOptions =>
{
builder.Configuration.Bind("AzureAd", configureMicrosoftIdentityOptions);
})
.EnableTokenAcquisitionToCallDownstreamApi(new string[] { builder.Configuration["DownstreamApi:Scopes"] })
.AddDownstreamApi("WeatherList", builder.Configuration.GetSection("DownstreamApi"))
.AddInMemoryTokenCaches();
builder.Services.AddControllersWithViews()
.AddMicrosoftIdentityUI();
builder.Services.AddAuthorization(options =>
{
options.FallbackPolicy = options.DefaultPolicy;
});
The above statement will register the authentication and authorization services.
app.UseAuthentication();
app.UseAuthorization();User Authentication and Authorization Middleware
Call the API
I created a page to render the weather forecast data
@page "/weatherList"
@inherits WeatherBase
<table class="table">
<thead>
<tr>
<th>
Date
</th>
<th>
Temperature
</th>
<th>
Summary
</th>
</tr>
</thead>
<tbody>
@foreach (var item in weatherList)
{
<tr>
<td>
@item.Date
</td>
<td>
@item.TemperatureC
</td>
<td>
@item.Summary
</td>
</tr>
}
</tbody>
</table>
WeatherBase.cs
public class WeatherBase: ComponentBase
{
[Inject]
MicrosoftIdentityConsentAndConditionalAccessHandler ConsentHandler { get; set; }
[Inject]
IDownstreamApi _downstreamApi { get; set; }
protected IEnumerable<WeatherModel> weatherList = new List<WeatherModel>();
protected WeatherModel toDo = new WeatherModel();
protected override async Task OnInitializedAsync()
{
await GetToDoListService();
}
/// <summary>
/// Gets all todo list items.
/// </summary>
/// <returns></returns>
[AuthorizeForScopes(ScopeKeySection = "DownstreamApi:Scopes")]
private async Task GetToDoListService()
{
try
{
weatherList = await _downstreamApi.GetForUserAsync<IEnumerable<WeatherModel>>("WeatherList");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
// Process the exception from a user challenge
ConsentHandler.HandleException(ex);
}
}
}
The above function GetToDoListService will be executed only if the user has the required scope. It is called the WeatherList Service, which is registered in theProgram.cs file using downstreamApi GetForUserAsync function.
WeatherModel.cs
public class WeatherModel
{
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string? Summary { get; set; }
}
Run both the applications, navigate to WeatherReport page from the blazor application, it will fetch the weatherReport based on application scope

Conclusion:
With this setup, you’ve integrated Microsoft Entra ID authentication into both a .NET 8 Web API and a Blazor Server app using secure OAuth 2.0 flows. The Blazor Server app acquires tokens on the user’s behalf to call protected endpoints in the API.