This tutorial will walk you through step by step on how to authenticate a user and display the user's information once authenticated. In this tutorial:
You can download the example code used in this topic on GitHub.
Before you begin, you need to install Microsoft.AspNetCore.Components.Authorization
. You also need to install additional NuGet packages based on your identity platform. For example, if you are using JWT, you will need to install System.IdentityModel.Tokens.Jwt
.
Microsoft.AspNetCore.Components.Authorization
and System.IdentityModel.Tokens.Jwt
from the NuGet.@using Microsoft.AspNetCore.Components.Authorization
to _Imports.razor
.CascadingAuthenticationState
as the root component in App.razor
. For example:<CascadingAuthenticationState> <Router AppAssembly="typeof(App).Assembly"> ... </Router> </CascadingAuthenticationState>
public class AuthenticationDataMemoryStorage { public string Token { get; set; } = ""; }
AuthenticationStateProvider
by create a new class that extends the AuthenticationStateProvider
class. The following image illustrates the main objective of the GetAuthenticationStateAsync
method.Code sample:
public class BlazorSchoolAuthenticationStateProvider : AuthenticationStateProvider { private readonly HttpClient _httpClient; private readonly AuthenticationDataMemoryStorage _authenticationDataMemoryStorage; public BlazorSchoolAuthenticationStateProvider(HttpClient httpClient, AuthenticationDataMemoryStorage authenticationDataMemoryStorage) { _httpClient = httpClient; _authenticationDataMemoryStorage = authenticationDataMemoryStorage; } public override Task<AuthenticationState> GetAuthenticationStateAsync() { var tokenHandler = new JwtSecurityTokenHandler(); var identity = new ClaimsIdentity(); if (tokenHandler.CanReadToken(_authenticationDataMemoryStorage.Token)) { var jwtSecurityToken = tokenHandler.ReadJwtToken(_authenticationDataMemoryStorage.Token); identity = new ClaimsIdentity(jwtSecurityToken.Claims, "Blazor School"); } var principal = new ClaimsPrincipal(identity); var authenticationState = new AuthenticationState(principal); var authenticationTask = Task.FromResult(authenticationState); return authenticationTask; } }
Code sample:
public async Task LoginAsync() { string token = await _httpClient.GetStringAsync("example-data/token.json"); _authenticationDataMemoryStorage.Token = token; NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); }
For example:
public void Logout() { _authenticationDataMemoryStorage.Token = ""; NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); }
AuthenticationStateProvider
at Program.cs
. For example:builder.Services.AddScoped<AuthenticationDataMemoryStorage>(); builder.Services.AddScoped<BlazorSchoolAuthenticationStateProvider>(); builder.Services.AddScoped<AuthenticationStateProvider>(sp => sp.GetRequiredService<BlazorSchoolAuthenticationStateProvider>()); builder.Services.AddAuthorizationCore();
After you have done all the steps, you can use the LoginAsync
method to authenticate the users. For example:
@inject BlazorSchoolAuthenticationStateProvider BlazorSchoolAuthenticationStateProvider <button @onclick="BlazorSchoolAuthenticationStateProvider.LoginAsync">Login</button>
The user information is stored at the ClaimPrincipal.Claims
. To access to the user information, you need to:
AuthenticationStateProvider
class, create a property to store the information that you want to access. For example:public string Username { get; set; } = "";
AuthenticationStateChanged
event. In this method, store the user information to the property you have created at the previous step. For example:private async void OnAuthenticationStateChanged(Task<AuthenticationState> task) { var authenticationState = await task; if (authenticationState is not null) { Username = authenticationState.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value ?? ""; } }
In the example, we store theClaim
with the typeClaimTypes.NameIdentifier
. If you use another claim type, you need to change the code accordingly.
AuthenticationStateChanged
event. For example:public BlazorSchoolAuthenticationStateProvider(HttpClient httpClient, AuthenticationDataMemoryStorage authenticationDataMemoryStorage) { ... AuthenticationStateChanged += OnAuthenticationStateChanged; }
IDisposable
interface to unsubscribe the AuthenticationStateChanged
event. For example:public class BlazorSchoolAuthenticationStateProvider : AuthenticationStateProvider, IDisposable { ... public void Dispose() { AuthenticationStateChanged -= OnAuthenticationStateChanged; } }
After you have done all the steps, you can access the user information by the property in your custom AuthenticationStateProvider
class. For example:
@inject BlazorSchoolAuthenticationStateProvider BlazorSchoolAuthenticationStateProvider <AuthorizeView> <Authorized> <div>User: @BlazorSchoolAuthenticationStateProvider.Username</div> </Authorized> </AuthorizeView>
In this section, we have collected some common mistakes from the Blazor School Discord Community.
ClaimsIdentity
without the authenticationType
In the GetAuthenticationStateAsync
method, you need to create a ClaimsIdentity
, and from the ClaimsIdentity
, create a ClaimsPrincipal
. However, most people created ClaimsIdentity
without the authenticationType
. For example:
// Wrong var identity = new ClaimsIdentity(claims);
This is the right way to create a ClaimsIdentity
:
// Correct var identity = new ClaimsIdentity(claims, "Blazor School");
In the example, the second parameter "Blazor School"
is the authenticationType
. The authenticationType
can be any string you want.
authenticationType
?The ClaimsIdentity
will mark the user as unauthenticated, the property ClaimsIdentity.IsAuthenticated
will be false
.
CORS (Cross-Origin Resource Sharing) is a technique to prevent the API being accessed by the website from outside the list. However, CORS is enforced by the browser only. The attacker can send a request from outside the browser to access your API.
GetAuthenticationStateAsync
from AuthenticationStateProvider
to get the user's informationThe GetAuthenticationStateAsync
is not intended to be called in the component by AuthenticationStateProvider
. For example, the following code is bad:
// Bad code @inject BlazorSchoolAuthenticationStateProvider BlazorSchoolAuthenticationStateProvider <div>@Username</div> @code { public string Username { get; set; } = ""; protected override async Task OnInitializedAsync() { var authenticationState = await BlazorSchoolAuthenticationStateProvider.GetAuthenticationStateAsync(); if (authenticationState is not null) { Username = authenticationState.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value ?? ""; } } }
GetAuthenticationStateAsync
from AuthenticationStateProvider
to get the user's information?The GetAuthenticationStateAsync
will be executed whenever a component is rendered. The following video demonstrates this mistake:
HttpContextAccessor
to get the user informationThe MVC and the Web API use HttpContextAccessor
to get the user information. This creates a confusion in Blazor if you don't know how Blazor works. Blazor is an SPA framework, there will be only 1 request and that request cannot contain the user information. To get the information, follow the Access the user information in this tutorial.