Skip to main content

Part-1 Blazor WebAssembly Cookie Authentication[.NET 6]

In this article, we are going to authenticate our Blazor WebAssemably application with simple Asp.NetCore cookie.

Targets Of Blazor WebAssembly Cookie Auth Series:

Let's have look at this Blazor WebAssmbly series:
  • Part -1 is simple asp.net core cookie authentication.
  • Part-2 is cookie authentication with FaceBook.
  • Part-3 is cookie authentication with Twitter.
  • Part-4 is cookie authentication with Google.
  • Part-5 is cookie authentication with Microsoft.

Blazor WebAssembly Cookie Authentication Flow:


  • We must have 2 applications 'Blazor WebAssembly Application'(which runs on the user browser), and 'Asp.Net Core API'(which runs on the server).
  • Blazor WebAssembly requests the API for user authentication by sending the user credentials to API.
  • For valid credentials, API generates auth cookie and sends it to the client application.
  • So after successful authentication of every request from Blazor WebAssembly to API, the auth cookie will be attached to every subsequent request to make the request as authenticated.

Create A Sample User Table:

Let's run the below SQL script to create a sample user table.
CREATE TABLE [dbo].[User](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[Email] [varchar](200) NULL,
	[FirstName] [varchar](100) NULL,
	[LastName] [varchar](100) NULL,
	[Password] [varchar](200) NULL,
	[ExternalLoginName] [varchar](50) NULL,
 CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED 
(
	[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

Create A .NET6 Web API Application:

Let's create a .Net6 Web API sample application to accomplish our demo. We can use either Visual Studio 2022 or Visual Studio Code(using .NET CLI commands) to create any .NET6 application. For this demo, I'm using the 'Visual Studio Code'(using the .NET CLI command) editor.
.NET CLI Command:
dotnet new webapi -o Your_Project_Name

Install EntityFramework Core NuGet Package Into API Project:

Now install the following package into the API project.

Let's install the Entity Framework Core NuGet package.
Package Manager Command:
Install-Package Microsoft.EntityFrameworkCore -Version 6.0.3

.NET CLI Command:
dotnet add package Microsoft.EntityFrameworkCore --version 6.0.3

Let's install the Entity Framework Core SQL NuGet package.
Package Manager Command:
Install-Package Microsoft.EntityFrameworkCore.SqlServer -Version 6.0.3

.NET CLI Command:
dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 6.0.3

Configure Database Conext In API App:

Let's create an entity of our table like 'User.cs'.
API_Project/Data/Entities/Users.cs:
namespace CookieApi.Data.Entities;

public class User
{
    public int Id { get; set; }
    public string? Email { get; set; }
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
    public string? Password{get;set;}
    public string? ExternalLoginName{get;set;}
}
Now let's create a Database Context entity like 'CookieAuthContext.cs'.
API_Project/Data/CookieAuthContext.cs:
using CookieApi.Data.Entities;
using Microsoft.EntityFrameworkCore;

namespace CookieApi.Data;

public class CookieAuthContext:DbContext
{
    public CookieAuthContext(DbContextOptions<CookieAuthContext> option):base(option)
    {
        
    }
    public DbSet<User> User {get;set;}
}
  • Inside the Database context, we need to register our table classes like we registered our 'User'.
Add the database connection string in 'appsettings.Development.json'.
API_Project/appsettings.Development.json:
"ConnectionStrings": {
  "CookieAuthConnection":""
}
Now register our database context entity in 'Program.cs'.
API_Project/Program.cs:
builder.Services.AddDbContext<CookieAuthContext>(options => {
    options.UseSqlServer(builder.Configuration.GetConnectionString("CookieAuthConnection"));
});

Create A AuthController In API Project:

Let's create a 'AuthController' in our API Project. In this controller, we are going to add our action methods like 'Login', 'Logout', 'UserProfile'.
API_Project/Controllers/AuthController.cs:
using CookieApi.Data;
using Microsoft.AspNetCore.Mvc;

namespace CookieApi.Controllers;

[ApiController]
[Route("[controller]")]
public class AuthController:ControllerBase
{
    private readonly CookieAuthContext _cookieAuthContext;
    public AuthController(CookieAuthContext cookieAuthContext)
    {
        _cookieAuthContext = cookieAuthContext;
    }
}
  • Here injected our 'CookieAuthContext' into the 'AuthController'.

Add User Login Endpoint In API Project:

Let's create DTO(data transfer object) for our HTTP POST login endpoint like 'LoginDto.cs'.
API_Project/Dtos/LoginDto.cs:
namespace CookieApi.Dtos;

public class LoginDto
{
    public string Email { get; set; }
    public string Password { get; set; }
}
Note: In this demo, I'm going to store the raw password in the table which is very bad practice, we have to hash the password stored into the database. For custom hashing, you can refer to my article.

Now let's add our 'LoginAsync' action method into the 'AuthController'.
API_Project/Controllers/AuthController.cs:
using System.Security.Claims;
using CookieApi.Data;
using CookieApi.Dtos;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

// code hidden for display purpose

[HttpPost]
[Route("login")]
public async Task<IActionResult> LoginAsync(LoginDto login)
{
	var user = await _cookieAuthContext
	.User.Where(_ => _.Email.ToLower() == login.Email.ToLower() &&
	_.Password == login.Password && _.ExternalLoginName == null)
	.FirstOrDefaultAsync();
	if (user == null)
	{
		return BadRequest("Invalid Credentials");
	}

	var claims = new List<Claim>
	{
		new Claim("userid", user.Id.ToString()),
		new Claim(ClaimTypes.Email, user.Email)
	};

	var claimsIdentity = new ClaimsIdentity(
		claims, CookieAuthenticationDefaults.AuthenticationScheme);

	var authProperties = new AuthenticationProperties();

	await HttpContext.SignInAsync(
		CookieAuthenticationDefaults.AuthenticationScheme,
		new ClaimsPrincipal(claimsIdentity),
		authProperties);
	return Ok("Success");
}
  • (Line: 15-18) Fetching the user from the database using 'Email', 'Password. Here we check 'ExternalLoginName' for 'null' which means it is a user email and password authentication because users can have different authentication like 'Google', 'Facebook', etc with the same email address.
  • (Line: 19-22) User credentials are invalid then we return as a bad request.
  • (Line: 24-28) Defining authenticated user claims that will be stored in the authentication cookie.
  • (Line: 31&32) Initialized 'ClaimsIdentity' that need 'User Claims' and 'AuthenticationType'.
  • (Line: 35-38) Invoking the 'HttpContext.SignInAsync()' method that generates the authentication cookie.

Add User Logout Endpoint In API Project:

Let's add HTTP Post 'LogoutAsync' action method into the 'AuthController'.
API_Project/Controllers/AuthController.cs:
[HttpPost]
[Route("logout")]
public async Task<IActionResult> LogoutAsync()
{
	await HttpContext.SignOutAsync();
	return Ok("success");
}
  • (Line: 5) Invokes the 'HttpContext.SingOutAsync()' method to logout the user by clearing the authenticated user cookie.

Add UserProfile Auth Protection Endpoint In API Project:

Let's create a response DTO for our user profile endpoint like 'UserProfileDto.cs'.
API_Projects/Dtos/UserProfileDto.cs:
namespace CookieApi.Dtos;

public class UserProfileDto
{
    public int UserId { get; set; }
    public string Email { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
Let's add authentication-protected action methods like 'UserProfileAsync' in 'AuthController'.
API_Project/Controllers/AuthController.cs:
[Authorize]
[HttpGet]
[Route("user-profile")]
public async Task<IActionResult> UserProfileAsync()
{
	int userId =HttpContext.User.Claims
	.Where(_ => _.Type =="userid")
	.Select(_ =>Convert.ToInt32( _.Value))
	.First();

	var userProfile = await _cookieAuthContext
	.User
	.Where(_ => _.Id == userId)
	.Select(_ => new UserProfileDto{
		UserId = _.Id,
		Email = _.Email,
		FirstName = _.FirstName,
		LastName = _.LastName
	}).FirstOrDefaultAsync();
	
	return Ok(userProfile);
}
  • (Line: 1) Our 'user-profile' endpoint is protected by user authentication by enabling the 'Authorize' attribute.
  • (Line: 6-9) Fetching the authenticated user id claim from the requests HttpContext.
  • (Line: 11-19) Defining the user profile information based on the authenticated user.

Configure Required Services And Middlewares In API Project:

Our API and Blazor Webassembly(application going to be created in upcoming steps) application run on different domains so we have to enable the 'Cors' service.
API_Project/Program.cs:
builder.Services.AddAuthentication(
    CookieAuthenticationDefaults.AuthenticationScheme
)
.AddCookie();
builder.Services.AddCors(options =>
{
    options.AddPolicy("CorsSpecs",
    builder =>
    {
        builder
            .AllowAnyHeader()
            .AllowAnyMethod()
            .SetIsOriginAllowed(options => true)
            .AllowCredentials();
    });
});

// Above the below line register Cors
var app = builder.Build(); 
  • (Line: 1-4) Registered cookie authentication.
  • (Line: 7) Defined name to our 'Cors' service.
  • (Line: 11&12) Allowing all 'Headers' and 'Methods'.
  • (Line: 13&14) In our blazor application after the successful authentication, every other request carries auth cookie along with it. To accept auth cookie from a request from a different domain must add the 'AllowCredentials()' method. That 'AllowCredentials()' method fails to work with 'AllowAnyOrigin()' method, so we have to configure 'SetIsOringAllowed(option => true)' if we use 'AllowedCredentials()'.
Now let's add 'Cors' and 'Authentication' middlewares.
API_Project/Program.cs:
app.UseCors("CorsSpecs");
app.UseAuthentication();

//just above the 'UseAuthorization' 
//middleware add our required middlewares
app.UseAuthorization();

Create A .NET6 Blazor WebAssembly Application:

Let's create a .Net6 Blazor WebAssembly sample application to accomplish our demo. We can use either Visual Studio 2022 or Visual Studio Code(using .NET CLI commands) to create any .NET6 application. For this demo, I'm using the 'Visual Studio Code'(using the .NET CLI command) editor.
.NET CLI Command:
dotnet new blazorwasm -o Your_Project_Name

Install Required Packages In The BlazorWasm App:

Let's install the Blazor WebAssembly Authentication library.
Package Manager:
Install-Package Microsoft.AspNetCore.Components.WebAssembly.Authentication -Version 6.0.3

.NET CLI Command:
dotnet add package Microsoft.AspNetCore.Components.WebAssembly.Authentication --version 6.0.3

Let's install the 'Microsoft.Extensions.Http' library.
Package Manager:
Install-Package Microsoft.Extensions.Http -Version 6.0.0

.NET CLI Command:
dotnet add package Microsoft.Extensions.Http --version 6.0.0

Let's install the 'Blazored.LocalStorage' library.
Package Manager:
Install-Package Blazored.LocalStorage -Version 4.2.0

.NET CLI Command:
dotnet add package Blazored.LocalStorage --version 4.2.0

Register Installed Packages Services In Blazor APP:

Let's add the below namespaces to the '_Imports.razor'
BlazorWasm_Project/_Imports.razor:
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Blazored.LocalStorage
Let's register the below services into the 'Program.cs'.
BlazorWasm_Project/Program.cs:
builder.Services.AddOptions();
builder.Services.AddAuthorizationCore();
builder.Services.AddBlazoredLocalStorage();

Implement AuthenticationStateProvider:

In blazor authentication carried by the 'Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider'. Let's implement AuthenticationStateProvider to customize the user login.

Let's create a custom AuthenticationStateProvider like 'CustomAuthStateProvider'.
BlazorWasm_Project/Providers/CustomAuthStateProvider:
using System.Security.Claims;
using Microsoft.AspNetCore.Components.Authorization;

namespace Bwasm.Cookie.Providers;

public class CustomAuthStateProvider : AuthenticationStateProvider
{
    private ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity());
    public override async Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        return new AuthenticationState(claimsPrincipal);
    }
}
  • (Line: 6) The 'CustomAuthStateProvider' implements the 'Microsoft.AspNetCore.Component.Authorization.AuthenticationStateProvider'.
  • (Line: 8) Initially empty 'ClaimsPrincipal' means no user claims(user not authenticated).
  • (Line: 9-12) Implemented the 'GetAuthenticationStateAsync()' method of type 'Task<AuthenticationState>'. So blazor invokes this method to get information about the authenticated user. If this method returns empty claims then authentication is not taken place and then if this method returns a collection of claims then the user is authenticated with the Blazor application.
Let's add the below namespace into the '_Import.razor'.
BlazorWasm_Project/_Import.razor:
@using Bwasm.Cookie.Providers
Now register the 'CustomAuthStateProvider' in 'Program.cs'.
BlazorWasm_Project/Program.cs:
builder.Services
.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>();
In 'App.razor' add the 'CascadingAuthenticationState' and 'AuthorizeRouteView' components to pass the authentication user information to the blazor components.
BlazorWasm_Project/App.razor:
<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>

Add Auth Cookie Credential To A Request By Implementing DelegateHandler In Blazor App:

Our Blazor web assembly application and API application run on different domains. So our blazor application invokes the API endpoint then the auth cookie of the API endpoint will not be sent by default. So to send auth cookie along with an API request we have to implement the HttpClient DelegateHandler.

Now let's create our DelegateHandler like 'CookieHandler'.
BlazorWasm_Project/Handlers/CookieHandler.cs
using Microsoft.AspNetCore.Components.WebAssembly.Http;

namespace Bwasm.Cookie.Handler;

public class CookieHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        request.SetBrowserRequestCredentials(BrowserRequestCredentials.Include);

        return await base.SendAsync(request, cancellationToken);
    }
}
  • (Line: 9) Explicitly defining to pass the browser credentials(auth credentials) for every request if they exist.
Now register our 'CookieHandler' in 'Program.cs'.
BlazorWasm_Project/Program.cs:
builder.Services.AddScoped<CookieHandler>();

Register HttpClient Service In Blazor App:

Let's register the 'Named' HttpClient where we define the API domain and the DelegateHandler.
BlazorWasm_Project/Program.cs:
builder.Services.AddHttpClient("API", options => {
    options.BaseAddress = new Uri("https://localhost:7244/");
})
.AddHttpMessageHandler<CookieHandler>();

Create ApiLogic In Blazor App:

Let's implement all our API logic into a class like 'ApiLogic'. So let's create files like 'IApiLogic.cs', 'ApiLogic.cs'.

BlazorWasm_Project/Logics/IApiLogic.cs:
namespace Bwasm.Cookie.Logics;
public interface IApiLogic
{

}
BlazorWasm_Project/Logics/ApiLogic.cs:
namespace Bwasm.Cookie.Logics;
public class ApiLogic: IApiLogic
{
    private readonly IHttpClientFactory _httpClientFactory;
    public ApiLogic(IHttpClientFactory httpClientFactory)
    {
        _httpClientFactory = httpClientFactory;
    }
}
  • Here injected the 'HttpClientFactory' instase:
Add the below namespace into the '_Import.razor'.
BlazorWasm_Project/_Import.razor:
@using Bwasm.Cookie.Logics
Register the 'ApiLogic' in Program.cs.
BlazorWasm_Project/Program.cs: 
builder.Services.AddScoped<IApiLogic, ApiLogic>();

Logic To Invoke Use Login API:

Let's create a payload model like 'LoginModel'.
BlazorWasm_Project/Models/LoginModel.cs:
namespace Bwasm.Cookie.Models;

public class LoginModel
{
    public string Email { get; set; }
    public string Password { get; set; }
}
Add the below namespace in '_Import.razor'.
BlazorWasm_Project/_Import.razor:
@using Bwasm.Cookie.Models
Now in 'IApiLogic' add a method definition like the below.
BlazorWasm_Project/Logics/IApiLogic.cs:
Task<string> LoginAsync(LoginModel login);
Now in 'ApiLogic' implement the 'LoginAsync' method.
BlazorWasm_Project/Logics/ApiLogic.cs:
 
using System.Text;
using System.Text.Json;
using Bwasm.Cookie.Models;

public async Task<string> LoginAsync(LoginModel login)
{
	var client = _httpClientFactory.CreateClient("API");
	string payload = JsonSerializer.Serialize(login);
	var content = new StringContent(payload, Encoding.UTF8, "application/json");
	var response = await client.PostAsync("/Auth/login", content);
	if (response.IsSuccessStatusCode)
	{
		return "Success";
	}
	else
	{
		return "failed";
	}
}
  • (Line: 8) Creating  HttpClient instance from the IHttpClientFactory using the name of the HttpClient instance registered in 'Program.cs'.
  • (Line: 9&10) Formating user payload.
  • (Line: 11) Invoking the user login endpoint.
  • (Line: 12-19) If authentication is successful return a message like 'Success' or else return 'failed'.

Create Login.razor Component In Blazor App:

Let's create the login form by creating the 'Login.razor'.
BlazorWasm_Project/Pages/Login.razor:(HTML Part)
@page "/login"
@inject IApiLogic _apiLogic
@inject AuthenticationStateProvider _authStateProvider
@inject NavigationManager _navigationManager
@inject ILocalStorageService _localStorageService;

<div class="row">
    <div class="col-md-6 offset-md-3">
        <legend>User Login</legend>
        <div class="mb-3">
            <label for="txtEmail" class="form-label">Email</label>
            <input @bind="loginModel.Email" type="text" class="form-control" id="txtEmail" />
        </div>

        <div class="mb-3">
            <label for="txtPassword" class="form-label">Password</label>
            <input @bind="loginModel.Password" type="password" class="form-control" id="txtPassword" />
        </div>
        <button type="button" @onclick="UserLogin" class="btn btn-primary">Login</button>
    </div>
</div>
  • (Line: 1) Define the route using the '@page' directive.
  • (Line: 2-5) Injected all the required services.
  • (Line: 7-21) Login form.
  • (Line: 12&17) Enable form model binding using the '@bind' directive on textboxes.
  • (Line: 19) Here 'Login' button click event registered with the 'UserLogin' method.
BlazorWasm_Project/Pages/Login.razor:(C# Part)
@code {
    private LoginModel loginModel = new LoginModel();

    private async Task UserLogin()
    {
        var message = await _apiLogic.LoginAsync(loginModel);
        if (message == "Success")
        {
            await _localStorageService.SetItemAsStringAsync("isauthenticated", "true");
            _navigationManager.NavigateTo("/",true);
        }
    }
}
  • (Line: 2) The 'LoginModel' is used to capture the form data.
  • (Line: 6) Invokes the user login endpoint.
  • (Line: 9) Using LocalStorageService we are setting the 'isauthenticated' value to the browser's local storage. This value helps to invoke the 'UserProfile API' (which will implement in upcoming steps) whose data is used to bind on our Blazor application.
  • (Line: 10) On successful authentication user navigated to the home page by forcibly reloading the page.

Update AuthenticationState With LoggedIn User In Blazor App:

Let's add a new method like 'SetAuthInfo' in 'CustomAuthStateProvider' for updating the 'AuthenticationState'.
Blazorwasm_Project/Providers/CustomAuthStateProvider.cs:
public void SetAuthInfo(UserProfileModel userProfile)
{
	var identity = new ClaimsIdentity(new[]{
		new Claim(ClaimTypes.Email, userProfile.Email),
		new Claim(ClaimTypes.Name, $"{userProfile.FirstName} {userProfile.LastName}"),
		new Claim("UserId", userProfile.ToString())
	}, "AuthCookie");

	claimsPrincipal = new ClaimsPrincipal(identity);
	NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
}
  • Here 'UserProfileModel' will be the response of the 'user-profile' endpoint(Which we implement in upcoming steps). Creating the new user claims based on the 'UserProfileModel'. Then the newly created ClaimsPrincipal was assigned to the 'claimsPrincipal' variable. The 'NotifyAuthenicationStateChanged' method refreshes the 'AuthenticationState' data.

Logic To Invoke The UserProfile Endpoint In Blazor App:

Let's add a method definition like 'UserProfileAsync' in 'IApiLogic'.
BlazorWasm_Project/Logics/IApiLogic.cs:
Task<(string Message, UserProfileModel? UserProfile)> UserProfileAsync();
Now implement the 'UserProfileAsync' in 'ApiLogic'.
BlazorWasm_Project/Logics/ApiLogic.cs:
public async Task<(string Message, UserProfileModel? UserProfile)> UserProfileAsync()
{
	var client = _httpClientFactory.CreateClient("API");
	var response = await client.GetAsync("/Auth/user-profile");
	if (response.IsSuccessStatusCode)
	{
		return ("Success", await response.Content.ReadFromJsonAsync<UserProfileModel>());
	}
	else
	{
		if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
		{
			return ("Unauthorized", null);
		}
		else
		{
			return ("Failed", null);
		}
	}
}

Invoke User Profile Endpoint At OnNavigateAsync Method In Blazor App:

Now our goal is to call the user profile endpoint inside of the 'OnNavigateAsync' method. But 'OnNavigateAsync' method gets executed for every navigation. So we have to conditionally invoke the user profile endpoint to reduce unwanted API calls. So we depend on the local storage variable that is 'isuserauthenticated' and 'AuthenticationState' value. So that we can maintain user authentication in our blazor webassembly very comfortably even if the page reloads.
BlazorWasm_Project/App.razor:(Html Part)
@inject ILocalStorageService _localStorageService;
@inject IApiLogic _apiLogic
@inject AuthenticationStateProvider _authStateProvider
<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(App).Assembly" OnNavigateAsync="@OnNavigateAsync">
        <!-- existing code hidden for display purpose -->
    </Router>
</CascadingAuthenticationState>
  • (Line: 1-3) Injected all required services.
  • (Line: 5) The 'OnNavigateAsync' event callback is registered with the 'OnNavigateAsync' method that executes for every navigation.
BlazorWasm_Project/App.razor:(C# Part)
@code{
    private async Task OnNavigateAsync(NavigationContext args)
    {
        var auth =  await _localStorageService.GetItemAsync<string>("isauthenticated");
        var user = (await( _authStateProvider as CustomAuthStateProvider)
        .GetAuthenticationStateAsync()).User;
        
        if(!string.IsNullOrEmpty(auth) && !user.Identity.IsAuthenticated ){
            var response = await _apiLogic.UserProfileAsync();
            if (response.Message == "Success")
            {
                ( _authStateProvider as CustomAuthStateProvider)
                .SetAuthInfo(response.UserProfile);
            }
            else if(response.Message == "Unauthorized"){
                await _localStorageService.RemoveItemAsync("isauthenticated");
            }
        }
    }
}
  • (Line: 4) Fetch the browser's local storage value that is 'isauthenticated'.
  • (Line: 5-6) Getting the authenticated user information.
  • (Line: 8-18) If the local storage variable exists and no user information from authenticated state provider which means the page is reloading so at that time we will lose our authentication state provider information so to get that info we will call user profile API then we can re-populate the authentication state provider with user information. So to re-populate the user information we can  call 'CustomAuthStateProvider.SetAuthInfo()' method.

Clear AuthenticationState On User Logout In Blazor App:

Let's implement logic to clear the AuthentictionState for the user logout.
BlazorWasm_Project/Providers/CustomAuthStateProvider.cs:
public void ClearAuthInfo()
{
	claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity());
	NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
}
  • Just assign the empty 'ClaimsPrincipal' object to clear the 'AuthenticationState'.

Logic To Invoke Logout Endpoint In Blazor App:

Let's add a method definition like 'LogoutAsync' in 'IAppLogic'.
BlazorWasm_Project/Logics/IAppLogic.cs:
Task<string> LogoutAsync();
Let's implement 'LogoutAsync' method in 'AppLogic'.
BlazorWasm_Project/Logics/AppLogic.cs:
public async Task<string> LogoutAsync()
{
	var client = _httpClientFactory.CreateClient("API");
	var response = await client.PostAsync("/Auth/logout", null);
	if (response.IsSuccessStatusCode)
	{
		return "Success";
	}
	return "Failed";
}

Create A LoginDisplay.razor Component In Blazor App:

Let's create LoginDisplay.razor component which we will use in the application menu to render the 'Login' links if the user is not authenticated or 'Logout', 'User Name' links if the user is authenticated.
BlazorWasm_Project/Shared/LoginDisplay.razor:(Html Part)
@inject AuthenticationStateProvider _authStateProvider
@inject NavigationManager _navigationManager
@inject IApiLogic _apiLogic
@inject ILocalStorageService _localStorageService;

<AuthorizeView>
    <Authorized>
        <a href="">@context.User.Identity?.Name!</a>
        <button type="button"  @onclick="Logout" class="nav-link btn btn-link">Log out</button>
    </Authorized>
    <NotAuthorized>
        <a href="/login">Log in</a>
    </NotAuthorized>
</AuthorizeView>
  • (Line: 1-4) Injected the required services.
  • (Line: 8) Rendering the authenticated user name.
  • (Line: 9) Here 'Log out' click event registered with the 'Logout' method.
BlazorWasm_Project/Shared/LoginDisplay.razor:(C# Part)
@code {
    private async Task Logout()
    {
        var response = await _apiLogic.LogoutAsync();
        if (response == "Success")
        {
            (_authStateProvider as CustomAuthStateProvider).ClearAuthInfo();
            await _localStorageService.RemoveItemAsync("isauthenticated");
            _navigationManager.NavigateTo("/", true);
        }

    }
}
  • (Line: 4) Invoked Logout API.
  • (Line: 7) Clearing the Authenication state provider.
  • (Line: 8) Clearing the local storage variable.
  • (Line: 9) Finally, reload the entire page to clear all user information.
Now render the 'LoginDisplay.razor' in the 'Shared/MainLayout.razor'.
BlazorWasm_Project/Shared/MainLayout.razor:
<LoginDisplay></LoginDisplay>
Step1:

Step 2:

Step 3:

Step 4:

Support Me!
Buy Me A Coffee PayPal Me

Video Session:

Wrapping Up:

Hopefully, I think this article delivered some useful information about Blazor WebAssembly Cookie Authentication. using I love to have your feedback, suggestions, and better techniques in the comment section below.

Refer:



Follow Me:

Comments

Popular posts from this blog

.NET6 Web API CRUD Operation With Entity Framework Core

In this article, we are going to do a small demo on AspNetCore 6 Web API CRUD operations. What Is Web API: Web API is a framework for building HTTP services that can be accessed from any client like browser, mobile devices, desktop apps. In simple terminology API(Application Programming Interface) means an interface module that contains a programming function that can be requested via HTTP calls to save or fetch the data for their respective clients. Some of the key characteristics of API: Supports HTTP verbs like 'GET', 'POST', 'PUT', 'DELETE', etc. Supports default responses like 'XML' and 'JSON'. Also can define custom responses. Supports self-hosting or individual hosting, so that all different kinds of apps can consume it. Authentication and Authorization are easy to implement. The ideal platform to build REST full services. Create A .NET6 Web API Application: Let's create a .Net6 Web API sample application to accomplish our

Blazor WebAssembly Custom Authentication From Scratch

In this article, we are going to explore and implement custom authentication from the scratch. In this sample, we will use JWT authentication for user authentication. Main Building Blocks Of Blazor WebAssembly Authentication: The core concepts of blazor webassembly authentication are: AuthenticationStateProvider Service AuthorizeView Component Task<AuthenticationState> Cascading Property CascadingAuthenticationState Component AuthorizeRouteView Component AuthenticationStateProvider Service - this provider holds the authentication information about the login user. The 'GetAuthenticationStateAsync()' method in the Authentication state provider returns user AuthenticationState. The 'NotifyAuthenticationStateChaged()' to notify the latest user information within the components which using this AuthenticationStateProvider. AuthorizeView Component - displays different content depending on the user authorization state. This component uses the AuthenticationStateProvider

How Response Caching Works In Asp.Net Core

What Is Response Caching?: Response Caching means storing of response output and using stored response until it's under it's the expiration time. Response Caching approach cuts down some requests to the server and also reduces some workload on the server. Response Caching Headers: Response Caching carried out by the few Http based headers information between client and server. Main Response Caching Headers are like below Cache-Control Pragma Vary Cache-Control Header: Cache-Control header is the main header type for the response caching. Cache-Control will be decorated with the following directives. public - this directive indicates any cache may store the response. private - this directive allows to store response with respect to a single user and can't be stored with shared cache stores. max-age - this directive represents a time to hold a response in the cache. no-cache - this directive represents no storing of response and always fetch the fr

.Net5 Web API Managing Files Using Azure Blob Storage

In this article, we are going to understand the different file operations like uploading, reading, downloading, and deleting in .Net5 Web API application using Azure Blob Storage. Azure Blob Storage: Azure blob storage is Microsoft cloud storage. Blob storage can store a massive amount of file data as unstructured data. The unstructured data means not belong to any specific type, which means text or binary data. So something like images or pdf or videos to store in the cloud, then the most recommended is to use the blob store. The key component to creating azure blob storage resource: Storage Account:- A Storage account gives a unique namespace in Azure for all the data we will save. Every object that we store in Azure Storage has an address. The address is nothing but the unique name of our Storage Account name. The combination of the account name and the Azure Storage blob endpoint forms the base address for each object in our Storage account. For example, if our Storage Account is n

A Small Guide On NestJS Queues

NestJS Application Queues helps to deal with application scaling and performance challenges. When To Use Queues?: API request that mostly involves in time taking operations like CPU bound operation, doing them synchronously which will result in thread blocking. So to avoid these issues, it is an appropriate way to make the CPU-bound operation separate background job.  In nestjs one of the best solutions for these kinds of tasks is to implement the Queues. For queueing mechanism in the nestjs application most recommended library is '@nestjs/bull'(Bull is nodejs queue library). The 'Bull' depends on Redis cache for data storage like a job. So in this queueing technique, we will create services like 'Producer' and 'Consumer'. The 'Producer' is used to push our jobs into the Redis stores. The consumer will read those jobs(eg: CPU Bound Operations) and process them. So by using this queues technique user requests processed very fastly because actually

.Net5 Web API Redis Cache Using StackExchange.Redis.Extensions.AspNetCore Library

In this article, we are going to explore the integration of Redis cache in .Net5 Web API application using the 'StackExchange.Redis.Exntensions' library. Note:- Microsoft has introduced an 'IDistributedCache' interface in dotnet core which supports different cache stores like In-Memory, Redis, NCache, etc. It is simple and easy to work with  'IDistributedCache', for the Redis store with limited features but if we want more features of the Redis store we can choose to use 'StackExchange.Redis.Extensions'.  Click here for Redis Cache Integration Using IDistributedCache Interface . Overview On StackExchange.Redis.Extnesions Library: The 'StackExchange.Redis.Extension' library extended from the main library 'StackExchange.Redis'. Some of the key features of this library like: Default serialization and deserialization. Easy to save and fetch complex objects. Search key. Multiple Database Access Setup Redis Docker Instance: For this sampl

Usage Of CancellationToken In Asp.Net Core Applications

When To Use CancellationToken?: In a web application request abortion or orphan, requests are quite common. On users disconnected by network interruption or navigating between multiple pages before proper response or closing of the browser, tabs make the request aborted or orphan. An orphan request can't deliver a response to the client, but it will execute all steps(like database calls, HTTP calls, etc) at the server. Complete execution of an orphan request at the server might not be a problem generally if at all requests need to work on time taking a job at the server in those cases might be nice to terminate the execution immediately. So CancellationToken can be used to terminate a request execution at the server immediately once the request is aborted or orphan. Here we are going to see some sample code snippets about implementing a CancellationToken for Entity FrameworkCore, Dapper ORM, and HttpClient calls in Asp.NetCore MVC application. Note: The sample codes I will show in

Endpoint Routing In Asp.Net Core

How Routing Works In  Core 2.1 And Below Versions?: In Asp.Net Core routing is configured using app.UseRouter() or app.UseMvc() middleware. app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); Here in Dotnet Core version 2.1 or below versions on the execution of route middleware request will be navigated appropriate controller matched to the route. An operation or functionality which is dependent on route URL or route values and that need to be implemented before the execution of route middleware can be done by accessing the route path from the current request context as below app.Use(async (context, next) => { if(context.Request.Path.Value.IndexOf("oldvehicle") != -1) { context.Response.Redirect("vehicle"); } else { await next(); } }); app.UseMvc(routes => { routes.MapRoute( name: "vehicleRoute", template: "vehicle", defaul

Asp.Net Core MVC Form Validation Techniques

Introduction: Form validations in any applications are like assures that a valid data is storing on servers. All programing frameworks have their own individual implementations for form validations. In Dotnet Core MVC application server-side validations carried on by the models with the help of Data Annotations and the client-side validations carried by the plugin jQuery Unobtrusive Validation. jQuery Unobtrusive Validation is a custom library developed by Microsoft based on the popular library  jQuery Validate . In this article, we are going to learn how the model validation and client-side validation works in Asp.Net Core MVC Application with sample examples. Getting Started: Let's create an Asp.Net Core MVC application project using preferred editors like Microsoft Visual Studio or Microsoft Visual Studio Code. Here I'm using Visual Studio. Let's create an MVC controller and name it as 'PersonController.cs' and add an action method as bel

NestJS File Upload

In this article, we are going to understand the steps to create a file uploading endpoint in the NestJS application. Key Features In NestJS File Upload: Let us know some key features of NestJS file upload before implementing a sample application. FileInterceptor: The 'FileInterceptor' will be decorated on top of the file upload endpoint. This interceptor will read single file data from the form posted to the endpoint. export declare function FilesInterceptor(fieldName: string, localOptions?: MulterOptions): Type<NestInterceptor>; Here we can observe the 'fieldName' first input parameter this value should be a match with our 'name' attribute value on the form file input field. So our interceptor read our files that are attached to the file input field. Another input parameter of 'MulterOptions' that provides configuration like file destination path, customizing file name, etc. FilesInterceptor: The 'FilesInterceptor' will be decorated on t