Add authentication to an existing Blazor standalone application

Mariekie Coetzee
6 min readMar 8, 2023

--

Creating an application with authentication is now easier than ever, thanks to the templates and tutorials provided by Microsoft. However, I recently encountered a challenge when I needed to add authentication to an application that was based on a template that did not have authentication built in. Through this process, I gained valuable insights that I would like to share.

In this blog post, we will discuss the process of adding Azure Active Directory Authentication to an existing Blazor standalone application. This involves a few essential steps, including registering the application in Azure and integrating the authentication handling into the Blazor application.

Register Application in Azure

When an application is registered in Azure Active Directory, it is assigned an application ID, and permissions are linked to this registration. This ID is then used in the application for verification purposes.

Azure setup

  1. Open Azure and navigate to Azure Active Directory
Azure Active Direcotry

2. Under Manage blade select App registration

App registration

3. Select New registration

New registration

4. Fill out the information on the registration screen.

Register an application

To give access to users in a specific tenant, select Single Tenant under the Supported account types heading.

Under the Redirect URI heading select Single-page application (SPA). We will update the URI little later.

5. Once registered, open the newly created registration and select API permissions under the Manage blade.

Select API permissions

6. Add User.Read permissions within Microsoft Graph (Delegated permissions)

Microsoft Graph is a RESTful web API and grants access to microsoft cloud services. It contains protected APIs and supports tokens issued by MS identity platform.

The User.Read permission allows the app to read the profile, and discover relationships such as the group membership, reports and manager of the signed-in user only.

User.Read permissions

7. Verify that the status of the newly added permission has a green tick ✅. If it does not you need to click on the grey button saying ‘ ✔️ Grant admin consent for …

Grand admin consent

8. On the Overview blade make a copy of the Application (client ID) and the Directory (tenant) ID. We will need to update the app with this id.

Application ID & Direcotry ID

9. We need to add the callback URI in the Authenication blade. Edit the Redirect URIs field in the Single-page application section and add the callback URI. In this example, the application will run locally http://localhost:5001/authentication/login-callback.

Callback URI

Ensure that the url path is the same as the applicationUrl field in the project Properties>launchsettings.json file .

  "profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "http://localhost:5001",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}

Update Blazor Application

To integrate Azure Active Directory Authentication into an existing Blazor application, several updates are required. This includes adding the necessary authentication packages, scripts, and components, as well as making a few additional configurations.

  1. Add Microsoft.Authentication.WebAssembly.Msal package to the application.

The Msal package integrates with the Microsoft identity platform and secures the app with Azure Active Directory. We can acquire security tokens from Microsoft Identity platform to authenticate users and access secured web apis.

Microsoft.AspNetCore.Components.WebAssembly.Authentication library is not applicable in this scenario because it supports authenticating and authorizing apps that uses OAuth 2.0 protocol for example OpenId Connect.

2. In the wwwroot > index.html file add a script tag for Microsofts Authentication WebAssembly Msal Authentication Service.

<body>
<div id="app">Loading...</div>
<script src="_framework/blazor.webassembly.js"></script>
<script src="_content/Microsoft.Authentication.WebAssembly.Msal/AuthenticationService.js"></script>
</body>

The AuthenticationService handles the low-level details of the OIDC protocol. The app internally calls methods defined in the script to perform the authentication operations.

3. We now need to add the MsalAuthentication service in Program.cs, specify Azure as the authentication and the scopes.

builder.Services.AddMsalAuthentication(
options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes
.Add("https://graph.microsoft.com/User.Read");
options.ProviderOptions.LoginMode = "redirect";
});

In the above example it is requesting access to read the user profile. the LoginMode = “redirect” overrides the default ‘popup’ setting. Instead of having a popup screen, the website will be redirected to the applicable SSO provider specified in Azure AD. Once authorization is successful will it redirect to the callback specified.

4. The Application ID generated by AAD is sensitive information. The Azure AD data can be read from your wwwroot\appsettings.json and ideally is stored in a secret manager to protect sensitive information. The snipped below is from appsettings.json

{
"AzureAd": {
"Authority": "https://login.microsoftonline.com/******-****-****-****-********",
"ClientId": "******-****-****-****-********",
"ValidateAuthority": true
}
}

5. Unauthorized users will be redirected to Azure Active Directory who will honor any single sign on thats been defined. To do this we need to add a RedirectToLogin.razorcomponent that will redirect the user to an authentication/login route when it it initialized using NavigationManager

@inject NavigationManager Navigation

@code {

protected override void OnInitialized()
{
Navigation.NavigateTo(
$"authentication/login?returnUrl={Uri.EscapeDataString(Navigation.Uri)}");
}

}r

6. The above code is reference a page with a authentication/login route. To cater for different authentication actions, we will add an Authentication.razorpage with a parameter called {action} instead of hardcoding the action i.e. ‘login’

@page "/authentication/{action}" 

@using Microsoft.AspNetCore.Components.WebAssembly.Authentication

<RemoteAuthenticatorView Action="@Action" />

@code {

[Parameter]
public string Action { get; set; }

}

Here is a breakdown of what the above component is doing :

This Microsoft.AspNetCore.Components.WebAssembly.Authentication library offers a set of basic services to assist with authenticating users and obtain tokens to call protected APIs. The RemoteAuthenticatorView component is provided by this package. Instead of listing the using statement in the razor page, it can be added to _Imports.razor , for a cleaner implementation.

The RemoteAuthenticatorView component includes default UI pieces for the different authentication states. It uses the RemoteAuthenticationState as the persisted state across the authentication operations.

The {action} variable represents the different authentication states. These paths can be configured to follow custom routes.

7. Lets ensure that only unauthorized users are prompted to login by updating the App.Razor file.

The CascadingAuthenticationState component manages exposing the AuthenticationState to the rest of the app and should be added if it is not there. It is part of the Microsoft.AspNetCore.Components.Authorization namespace and should be added to _Imports.razor.

When the user is not authorized it will fall through to NotAuthorized node. Lets add theRedirectToLogin page within NotAuthorized, which will redirect the user to the login place.

<CascadingAuthenticationState>
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData"
DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
<RedirectToLogin />
</NotAuthorized>
<Authorizing>
<p>welcome back</p>
</Authorizing>
</AuthorizeRouteView>
</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>

8. The last step is to add the authorization to the required Blazor pages by adding in the Authorize attribute to the page. Having this attribute means that the user must be authorised to view the page. We can even specify the role and/or policy required to view the page.


@page "/"
@attribute [Authorize]

The authorize attribute uses the Microsoft.AspNetCore.Authorization package. Adding a using statement to _Imports.razor will make it globally accessable and will not need to be added to every razor file where it is used. Ensure that all three these using statements are in _Imports.razor to work.

@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication

the Microsoft.AspNetCore.Components.Authorization is required to retrieve information about the current authentication state.

Adding authentication to an existing Blazor application was an exciting and enlightening learning experience. Understanding when to use different Microsoft authentication packages and gaining insight into how authentication flow is handled in Blazor was incredibly valuable.

--

--

Mariekie Coetzee

A Software Engineer and gets childlike excited about developing apps. Loves the outdoors, camping and dreaming about the impossible.