In this article, we implement the logic for the user logout from the Blazor server application.
In this part of our article, we have to accomplish our targets like:
- SignOut Implementation.
- Generating Anti-Forgery Token.
Create Sign-Out Razor Page:
Since for logout we no need any UI page so we can implement our c# logic directly into the razor page so creating 'Logout.csthml' single file is enough.
Areas/Identity/Pages/Accoun/Logout.cshtml:
@page "/identiy/account/logout" @using Microsoft.AspNetCore.Authentication @using Microsoft.AspNetCore.Authentication.Cookies @inject IHttpContextAccessor _accessor @functions { public async Task<IActionResult> OnPostAsync() { await _accessor.HttpContext .SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); return Redirect("~/"); } }
- Here to sign out the user from the application w will use the 'Httpcontext.SignOutAsyn()' method by sending the authentication name as an input parameter.
Logout Link:
Let's add the sign-out link in the 'Shared/LoginDisplay.razor' blazor component.
Shared/LoginDisplay.razor:
<Authorized> <a href="Identity/Account/Manage">Hello, @context.User.Identity?.Name!</a> <form method="post" action="/identiy/account/logout"> <button type="submit" class="nav-link btn btn-link">Log out</button> </form> </Authorized>
- Here we can observe the 'Logout' button wrapped around 'form' which means logout is a post-call.
So the reason behind the 400 response, is our log-out form post request invokes without any anti forgery token. The Asp.Net Core application because for security reasons by default verifies the anti-forgery token for the HTTP Post calls. But adding an anti-forgery token into the Blazor application is not simple as MVC or Razor Pages, because HttpContext is not available for the blazor components.
Implement Anti-Forgery Token Into The Blazor Components:
So to make our logout successful, let's try to implement the anti-forgery token into the blazor component.
Let's create a model like 'ApplicationInitialState.cs'.
Models/ApplicationInitialState.cs:
namespace Dot6.Bserver.Cookie.Auth.Models; public class ApplicationInitialState { public string AntiForgeryToken { get; set; } }Now in the '_Host.cshtml' let's create the Anti-Forgery token and assign it to the 'ApplicationInitialState' instance.
Pages/_Host.cshtml:
@page "/" @namespace Dot6.Bserver.Cookie.Auth.Pages @using Dot6.Bserver.Cookie.Auth.Models @inject Microsoft.AspNetCore.Antiforgery.IAntiforgery Xsrf @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @{ Layout = "_Layout"; var appInitialState = new ApplicationInitialState() { AntiForgeryToken = Xsrf.GetAndStoreTokens(HttpContext).RequestToken }; } <component type="typeof(App)" param-InitialState="@appInitialState" render-mode="ServerPrerendered" />
- (Line: 4) Injecting 'Microsoft.AspNetCore.Antiforgery.IAntiforgery'.
- (Line: 8-11) Initialized 'ApplicationInitialState' and then assigned anti forgery token value to it.
- (Line: 14) Using 'param-InitialState' component property assigning our 'ApplicationState' value.
Models/TokenProvider.cs:
namespace Dot6.Bserver.Cookie.Auth.Models; public class TokenProvider { public string AntiForgeryToken { get; set; } }Let's register 'TokenProvider' into the DI in 'Program.cs'.
Program.cs:
builder.Services.AddScoped<TokenProvider>();Let's update our 'App.razor'.
App.razor:
@using Dot6.Bserver.Cookie.Auth.Models @inject TokenProvider _tokenProvider; <CascadingAuthenticationState> <Router AppAssembly="@typeof(App).Assembly"> <Found Context="routeData"> <RouteView 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> @code { [Parameter] public ApplicationInitialState InitialState { get; set; } protected override async Task OnInitializedAsync() { _tokenProvider.AntiForgeryToken = InitialState.AntiForgeryToken; } }
- (Line: 2) Injected the 'TokenProvider'.
- (Line: 18-19) Declared the component input parameter 'InitialState'.
- (Line: 23) Passing the anti-forgery token value to the 'TokenProvider'.
Shared/LoginDisplay.razor:
@inject Dot6.Bserver.Cookie.Auth.Models.TokenProvider _tokenProvider; <!-- code hidden for display purpose --> <form method="post" action="/identiy/account/logout"> <input name="__RequestVerificationToken" type="hidden" value="@_tokenProvider.AntiForgeryToken" /> <button type="submit" class="nav-link btn btn-link">Log out</button> </form>
- (Line: 1) Injected 'TokenProvider'.
- (Line: 4) Rendered hidden input field to which assigned our anti-forgery token value.
Support Me!
Buy Me A Coffee
PayPal Me
Video Session:
Wrapping Up:
Hopefully, I think this article delivered some useful information on Blazor Server Cookie Authentication. using I love to have your feedback, suggestions, and better techniques in the comment section below.
Thanks for saving our butts.
ReplyDelete