DEV Community

Cover image for Set up Azure Entra Authentication with Blazor
Ray_Dsz
Ray_Dsz

Posted on

Set up Azure Entra Authentication with Blazor

Introduction

As more enterprises embrace the Microsoft ecosystem, user management, file sharing, and productivity tools have become more seamlessly integrated than ever. With Microsoft now offering its own ERP solution—Dynamics 365 (D365)—many organizations are exploring hybrid environments that combine on-premises systems with cloud-based services.

In our case, we adopted a hybrid setup: SAP as our ERP system, and Microsoft Azure for user management, file storage, and more. Most of our critical company data remained on-premises, but we had a growing need to modernize our authentication approach.

Our primary requirement was to move from Windows Authentication to Multi-Factor Authentication (MFA) for improved security. To achieve this, we implemented Azure Entra Authentication.

In this blog, I’ll walk you through the step-by-step process of integrating Azure Entra Authentication into a Blazor Server application

Creating Azure app registration

Before your Blazor Server application can authenticate users or communicate securely with Azure services, it must first establish trust with Azure Active Directory (Azure AD). This is done through a process known as App Registration.

Think of this as the initial handshake—a formal agreement where Azure recognizes your application, grants it an identity, and provides the necessary credentials (like Client ID and Secret) to authenticate and authorize securely.

In this step, we’ll register the application in Azure to enable secure, machine-to-machine communication using Azure Entra ID. Once registered, we’ll receive the necessary configuration values (such as Tenant ID, Client ID, and optionally, Client Secret) which we’ll use in our Blazor Server app. Below is the step-by-step approach:

  • First register the azure app, Go to https://portal.azure.com/
  • Search for App Registrations and Click on new Registration
  • Type your app name, Select account type. You can add Redirect URI later.
  • Once you register, You will get a screen like this..

App Registration Screen

  • Copy Application (client) ID and Directory (tenant) ID. You need to add it in Blazor app.

  • Once done, Click on Certificates & secrets and Create a new client secret and copy the value. Remember, You will not be able to copy later. You need to add it in Blazor app.

  • Please keep in mind, Always Choose secret value for development. For production, I always suggest client certificate as it is much secure and doesn't expire for nearly 3 years.

  • You can even auto rotate the certificates by keeping in KeyVault. So that it never expires.

  • The client secret expires after 1 year. This leads to your app failure.

  • It's a best practice to register a separate Azure App for each Blazor Server application, rather than using a single shared app registration across multiple apps.

  • If something goes wrong—such as a client secret expiration, authentication failure—it will only affect that specific application, not every app relying on a shared registration.

  • Once done, click on Overview -> Select Redirect URIs. Add a redirect uri.

If your blazor app is running locally, It would be something like this 
https://localhost:7039/signin-oidc

For Postman,
https://oauth.pstmn.io/v1/callback
Enter fullscreen mode Exit fullscreen mode
  • (Optional) After this, Go to Token Configuration. Select add option claim -> Access -> Select whichever claims you want to receive in app and click Add.

  • Go to Api Permissions -> Click on Add a Permission -> Microsoft Graph -> Delegated -> Select User.Read.All and Profile and select Add

  • The following steps are only necessary if you want to bring roles from azure. This means you are maintaining roles and user mapping in Azure.

  • Go to Expose an Api -> Add a scope -> Add required Fields and click create.

  • The scope will be similar to below

 api://c60dde-e4af-47-8d-7c170b/User.All
Enter fullscreen mode Exit fullscreen mode
  • Go to App roles, Add your application roles here

App roles Screen

  • To Assign the roles to user, Click on How do I assign App roles and click on Enterprise application. You will get a screen like below

Enterprise app screen

  • Then, Under Manage -> Users and groups -> Add user/group -> map role to user.

  • Voila, You azure part is done.

Setting up Azure Entra authentication in Blazor.

  • Now, Comming to step 2. I already assume you have created a blazor server project.
  • Once done, We will start to set it up. Create a DependencyInjection class for blazor with two methods
    public static IServiceCollection AddUIServices(this IServiceCollection services, IConfiguration configuration, IConfigurationBuilder builder)
{
        return services;

}
    public static WebApplication UseUIServices(this WebApplication app, IConfiguration configuration)
{
        return app;
}
Enter fullscreen mode Exit fullscreen mode
  • In Program.cs
        var builder = WebApplication.CreateBuilder(args);
        builder.Services.AddUIServices(builder.Configuration, builder.Configuration);
        var app = builder.Build();

        app.UseUIServices(builder.Configuration);

        app.Run();
Enter fullscreen mode Exit fullscreen mode
  • Packages Required:
Microsoft.Identity.Web.UI
Microsoft.Identity.Web
Enter fullscreen mode Exit fullscreen mode
  • In DependencyInjection.cs
//In AddUIServices method       services.AddMicrosoftIdentityWebAppAuthentication(configuration)
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches(options =>
{
    options.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(15);
});
        services.AddControllersWithViews(options =>
        {
            var policy = new AuthorizationPolicyBuilder()
                .RequireAuthenticatedUser()
                .Build();
            options.Filters.Add(new AuthorizeFilter(policy));
        }).AddMicrosoftIdentityUI();

// In UseUIServices method, Make sure you have these
        app.UseHttpsRedirection();

        app.UseStaticFiles();
        app.UseRouting();
        app.UseAuthentication();
        app.MapControllers();
        app.UseAntiforgery();
Enter fullscreen mode Exit fullscreen mode
  • In appSettings.json
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "<YOUR_TENANT_ID>",
    "ClientId": "<YOUR_CLIENT_ID>",
    "ClientSecret": "<YOUR_CLIENT_SECRET_VALUE>",
    "Domain": "<YOUR_DOMAIN> usually your_company.com",
    "CallbackPath": "/signin-oidc"
  },
  "AppScopes": {
    "scopeBaseUrl": "api://c60dde-e4af-47-8d-7c170b/User.All"
  }
Enter fullscreen mode Exit fullscreen mode
  • Done, You basic configuration is done. Now we move to MainLayout.razor

  • I use Radzen as component library, So my MainLayout would look like this:

@inherits LayoutComponentBase
@inject AuthenticationStateProvider AuthenticationStateProvider
@inject IRequestService requestService

<RadzenLayout class="carrental-layout">
    <CascadingAuthenticationState>
        <AuthorizeView>
            <NotAuthorized>
                <Login />
            </NotAuthorized>
            <Authorized>
                <CascadingValue Value="empDetails" Name="EmployeeDetails">
                    @if (hasError)
                    {
                        <Error problemDetails="@problemDetails" />
                    }
                    else
                    {
                        <CascadingValue Value="userToken" Name="Token">
                            <Header @bind-empDetails="empDetails" />
                        </CascadingValue>

                        <RadzenBody class="carrental-body">
                            @Body
                        </RadzenBody>
                    }


                </CascadingValue>
            </Authorized>
        </AuthorizeView>
    </CascadingAuthenticationState>
    <RadzenFooter class="carrental-footer">
        <Footer />
    </RadzenFooter>
</RadzenLayout>

<RadzenComponents @rendermode="@RenderMode.InteractiveServer" />
Enter fullscreen mode Exit fullscreen mode
  • In the OnInitializedAsync method of MainLayout, We need to add this code
    protected async override Task OnInitializedAsync()
    {
        var auth = await AuthenticationStateProvider.GetAuthenticationStateAsync();
        var user = auth.User;
        if (auth.User.Identity.IsAuthenticated)
        {
            string userId = user.Identity.Name.Replace(TemplateConstants.s_samaccount, string.Empty);
                var result = await requestService.GetMyDetailsAsync(userId);

        }
}
Enter fullscreen mode Exit fullscreen mode
  • In the above code, Request Service is my custom api. This api is used to show you how to get the access token from azure.

  • In the Request Service Api class,

    private readonly HttpClient _httpClient;
    private readonly AppScopes appScopes;
    private readonly BasicApiUrls basicApiUrls;
    private readonly ITokenAcquisition tokenAcquisition;

    public RequestService(BasicApiUrls basicApiUrls,
        ITokenAcquisition tokenAcquisition,
        AppScopes appScopes,
        IHttpClientFactory _httpClientFactory)
    {
        this.basicApiUrls = basicApiUrls;
        this.tokenAcquisition = tokenAcquisition;

        _httpClient = _httpClientFactory.CreateClient("RequestClient");
        this.appScopes = appScopes;
    }
    public string Token { get; set; } = "";

    public async Task GetAzureToken()
    {

        try
        {
            Token = await tokenAcquisition.GetAccessTokenForUserAsync(new string[] { appScopes.ScopeBaseUrl! });
            _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token);
        }
        catch (Exception ex)
        {
            exceptionLogger.Log(ex);
        }
    }

    public async Task<Result<EmployeeDetails>> GetMyDetailsAsync(string employeeId)
    {
        try
        {
            await GetAzureToken();
            var request = new HttpRequestMessage(HttpMethod.Get, $"{basicApiUrls.WorkflowApiBaseUrl}/employee/{employeeId}");
            request = AppendCorrelationId(request);
            var response = await _httpClient.SendAsync(request);
            return await response.HandleApiResponse<EmployeeDetails>(logger);
        }
        catch (HttpRequestException exception)
        {
            var problemDetails = exceptionLogger.Log(exception);
            // Handle network-related errors
            return Result<EmployeeDetails>.FailureStatus(problemDetails);
        }

    }
Enter fullscreen mode Exit fullscreen mode
  • This is how you pass the token to the respective api

Validating the token passed from Blazor in Api

  • Finally, Comming to the last part that is validating the token in api.

  • Similar to blazor, I have created DependencyInjection class with two methods and i have called them in Program.cs

  • Packages required:

Microsoft.Identity.Web;
Enter fullscreen mode Exit fullscreen mode
  • Similar to blazor, Please add Azure Configuration in appSettings of Api as well.

  • Add below lines in AddApiService method

services.AddAuthentication().AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));

            services.AddAuthorization(options =>
            {
                // Azure AD-based policy
                options.AddPolicy("AzureAuth", policy =>
                {
                    policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme); // "Bearer"
                    policy.RequireAuthenticatedUser();
                    policy.RequireClaim("http://schemas.microsoft.com/identity/claims/scope", "User.All");
                    policy.RequireRole("Template.User");
                });
            });
Enter fullscreen mode Exit fullscreen mode
  • In UseApiServices method
            app.UseAuthentication();
            app.UseAuthorization();
Enter fullscreen mode Exit fullscreen mode
  • Finally, in Minimal Api
        public void AddRoutes(IEndpointRouteBuilder app)
        {
            var group = app.MapGroup("api/request/v{version:apiVersion}/employee")
                                .WithApiVersionSet()
                                .HasApiVersion(new ApiVersion(1));
            group.MapGet("{id}", UserDetails).CacheOutput().RequireAuthorization("AzureAuth");
}
Enter fullscreen mode Exit fullscreen mode
  • That's all it takes, This is a complete set up of azure entra from Azure app registration -> Blazor -> Api.

Conclusion

There are multiple ways to validate tokens and handle authentication flows—it ultimately depends on your architecture, security requirements, and preferred implementation style.

In this blog, I used .NET 8, Blazor Server, and ASP.NET Core Web API to demonstrate how to integrate Azure Entra Authentication in a clean and effective way.

I hope this guide not only helps you get started but also works seamlessly in your setup.

Happy coding! 🚀

Top comments (0)