I'm trying to put together a Blazor app that uses Entra Id with a popup form for authentication. I have had some success with an old version of Blazor but I would like to do this with the release version of Blazor (8). The "split" project file has me a bit confused. I'm wondering if Msal works with Blazor V8 release as server side or client side or both? Anyone have experience or success incorporating Entra ID authentication with a Blazor server or webasm app?
Does Msal work with Blazor V8 release? And where should the Msal components be located?
923 Views Asked by John S AtThere are 2 best solutions below

I had the following test today which is based on a .net 7 blazor server and migrate to .net 8 blazor web app.
Firstly, I created a new blazor server .net 7 and choose the "Microsoft identity platform" as the authentication type when using default Blazor template in VS. Then I updated the nuget packages (Microsoft.Identity.Web and .UI) to the latest version (now it's 2.16.0) and modify the configuration file so that we are able to sign into the application.
Now we already integrated Azure AD into this blazor server app. The next is following the migration tutorial to convert the app to use .net 8.
Firstly, in csproj file, changing to use <TargetFramework>net8.0</TargetFramework>
instead of net7.0
, then upgrade the left nuget packages Microsoft.AspNetCore.Authentication.JwtBearer and .OpenIdConnect
to Version="8.0.0"
instead of 7.0.0
.
Creating Routes.razor
then and move the codes from App.razor
into this file . Comment the <CascadingAuthenticationState>
because we use CascadingParameter
instead.
@* <CascadingAuthenticationState> *@
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
@* </CascadingAuthenticationState> *@
Then in the _Imports.razor
, adding @using static Microsoft.AspNetCore.Components.Web.RenderMode
.
Delete the _Host.cshtml
but moving the codes from _Host.cshtml
to App.razor
in advance.
@* @page "/"
@using Microsoft.AspNetCore.Components.Web
@namespace BlazorServer7To8.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers *@
@inject IHostEnvironment Env
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
@* <base href="~/" /> *@
<base href="/" />
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
<link href="css/site.css" rel="stylesheet" />
<link href="BlazorServer7To8.styles.css" rel="stylesheet" />
<link rel="icon" type="image/png" href="favicon.png"/>
@* <component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" /> *@
<HeadOutlet @rendermode="InteractiveServer" />
</head>
<body>
@* <component type="typeof(App)" render-mode="ServerPrerendered" /> *@
<Routes @rendermode="InteractiveServer" />
<div id="blazor-error-ui">
@* <environment include="Staging,Production">
An error has occurred. This application may no longer respond until reloaded.
</environment>
<environment include="Development">
An unhandled exception has occurred. See browser dev tools for details.
</environment> *@
@if (Env.IsDevelopment())
{
<text>
An unhandled exception has occurred. See browser dev tools for details.
</text>
}
else
{
<text>
An error has occurred. This app may no longer respond until reloaded.
</text>
}
<a href="" class="reload">Reload</a>
<a class="dismiss"></a>
</div>
@* <script src="_framework/blazor.server.js"></script> *@
<script src="_framework/blazor.web.js"></script>
</body>
</html>
Then modifying the Program.cs
. Replacing builder.Services.AddServerSideBlazor();
with builder.Services.AddRazorComponents().AddInteractiveServerComponents();
, replacing app.MapBlazorHub();
with app.MapRazorComponents<App>().AddInteractiveServerRenderMode();
, comment app.MapFallbackToPage("/_Host");
, put app.UseAntiforgery();
below app.UseRouting
, adding builder.Services.AddCascadingAuthenticationState();
for the authentication.
using BlazorServer7To8.Data;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
using BlazorServer7To8;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddDistributedTokenCaches();
builder.Services.AddControllersWithViews()
.AddMicrosoftIdentityUI();
builder.Services.AddAuthorization(options =>
{
// By default, all incoming requests will be authorized according to the default policy
options.FallbackPolicy = options.DefaultPolicy;
});
builder.Services.AddRazorPages();
//builder.Services.AddServerSideBlazor()
// .AddMicrosoftIdentityConsentHandler();
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents()
.AddMicrosoftIdentityConsentHandler();
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddSingleton<WeatherForecastService>();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAntiforgery();
app.MapControllers();
//app.MapBlazorHub();
//app.MapFallbackToPage("/_Host");
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode();
app.Run();
Finally I changed the default LoginDisplay.razor component like below.
<AuthorizeView>
<Authorized>
Hello, @context.User.Identity?.Name!
@authMessage
<a href="MicrosoftIdentity/Account/SignOut">Log out</a>
</Authorized>
<NotAuthorized>
<a href="MicrosoftIdentity/Account/SignIn">Log in</a>
</NotAuthorized>
</AuthorizeView>
@code {
private string authMessage = "The user is NOT authenticated.";
[CascadingParameter]
private Task<AuthenticationState>? authenticationState { get; set; }
protected override async Task OnInitializedAsync()
{
if (authenticationState is not null)
{
var authState = await authenticationState;
var user = authState?.User;
if (user?.Identity is not null && user.Identity.IsAuthenticated)
{
authMessage = $"{user.Identity.Name} is authenticated.";
}
}
}
}
If we are creating a new Blazor web app in .net 8 and want to integrate Azure AD, we need to change 7 files.
In appsettings.json, adding AAD related configurations:
In csproj file, adding nuget packages:
In
Program.cs
, adding related services.In
_Imports.cshtml
, adding@using Microsoft.AspNetCore.Components.Authorization
.In
Routes.razor
, addingAuthorizeRouteView
Creating
LoginDisplay.razor
in Pages folder.Adding LoginDisplay component into
MainLayout.razor
================== Update above ===================
We can create a blazor app integrating Azure AD by the default template. Here's the screenshot for stand-alone blazor wsam(client side). Or we could refer to this document to learn more about
how to create a standalone Blazor WebAssembly app that uses Microsoft Accounts with Microsoft Entra (ME-ID) for authentication
.But we don't have this option when choosing blazor web app.
Blazor web app is a replacement for Blazor server and Blazor wsam asp.net core hosted.
For Blazor server in .net 8(blazor web app), we have document which mentioned: