[Server Side] Support Custom Login Component when using Identity
See original GitHub issueDescription
Since I’m not a huge fan of the scaffolder to create and customize Identity pages, it would be nice to have a Login Component. So we can use the SignInManager to login and not rely on the Scaffolder.
Currently, if you use a SignInManager in a custom made Login Component with the following code on a button click:
@inject SignInManager<IdentityUser> SignInManager
// Some textfields and button here
public async Task DoLogin()
{
await SignInManager.PasswordSignInAsync(Model.Email, Model.Password, true, false);
}
Due to SignalR, the following exception is thrown:
System.InvalidOperationException: Headers are read-only, response has already started.
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.ThrowHeadersReadOnlyException()
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpHeaders.Microsoft.AspNetCore.Http.IHeaderDictionary.set_Item(String key, StringValues value)
at Microsoft.AspNetCore.Http.ResponseCookies.Append(String key, String value, CookieOptions options)
at Microsoft.AspNetCore.Authentication.Cookies.ChunkingCookieManager.AppendResponseCookie(HttpContext context, String key, String value, CookieOptions options)
at Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler.HandleSignInAsync(ClaimsPrincipal user, AuthenticationProperties properties)
at Microsoft.AspNetCore.Authentication.AuthenticationService.SignInAsync(HttpContext context, String scheme, ClaimsPrincipal principal, AuthenticationProperties properties)
at Microsoft.AspNetCore.Identity.SignInManager`1.SignInWithClaimsAsync(TUser user, AuthenticationProperties authenticationProperties, IEnumerable`1 additionalClaims)
at Microsoft.AspNetCore.Identity.SignInManager`1.SignInOrTwoFactorAsync(TUser user, Boolean isPersistent, String loginProvider, Boolean bypassTwoFactor)
at Microsoft.AspNetCore.Identity.SignInManager`1.PasswordSignInAsync(TUser user, String password, Boolean isPersistent, Boolean lockoutOnFailure)
at Microsoft.AspNetCore.Identity.SignInManager`1.PasswordSignInAsync(String userName, String password, Boolean isPersistent, Boolean lockoutOnFailure)
at Ecocolors.Server.Pages.Users.Users_Login.DoSomething() in C:\Users\Append\source\repos\vertonghenb\Ecocolors\source\Ecocolors.Server\Pages\Users\Users.Login.razor:line 74
at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
at Metronic.Components.KTButton.ClickCallback(UIMouseEventArgs args) in C:\Users\Append\source\repos\vertonghenb\Ecocolors\source\Metronic\Components\Buttons\KTButton.razor:line 50
at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
at Microsoft.AspNetCore.Components.Rendering.Renderer.GetErrorHandledTask(Task taskToHandle)
Solution
- Create a build-in LoginComponent we can override.
- Provide a workaround solution for the exception.
Issue Analytics
- State:
- Created 4 years ago
- Reactions:22
- Comments:20 (3 by maintainers)
Top Results From Across the Web
Adding a custom login page to Blazor Server app.
If you want to customize the Identity pages, you need to add a bit of scaffolding. The easiest way to do this is...
Read more >How To Add Custom Authentication In Blazor
This code creates a custom authentication handler that uses a user service to validate tokens and create a claims identity. The ...
Read more >How to implement custom authentication in Blazor Server ...
Identity ) #Blazor #AspNetCore # Authentication Join this channel to get access to ... Creating an Auth Service ( use databases instead in...
Read more >Custom blazor server-side authentication
I would like to add roles or claims now to be able to include them in the component, but find it difficult to...
Read more >Scaffold Identity in ASP.NET Core projects
Support Custom Login Component when using Identity ... to actually implement custom login form for server-side blazor (dotnet/AspNetCore.
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
I found a solution to make Identity work with pure Blazor components in Core 3.1. I’ll write a blog post about it and share it later. Here’s the principle:
Create a Login.razor component and inject SignInManager and NavigationManager. Use SignInManager to verify the password using the method CheckPasswordSignInAsync(). Do NOT call PasswordSignInAsync() as it will throw the exception mentioned earlier. Instead, pass the credentials to a credentials-cache in a custom middleware (see next paragraph). Then call NavigationManager.NagigateTo(/login?key=<someGuid>, true ) to execute a full postback, which is required for setting the cookie.
Create a Middleware class (I called it BlazorCookieLoginMiddleware): In there you use a static dictionary to cache login info from the Blazor login component. Also, you intercept the request to “/login?key=<guid>” and then perform the actual sign in using the SignInManager. This works because the middleware is executed earlier in the pipeline, when cookies can still be set. The credentials can be retrieved from the static dictionary cache and should immediately be removed from the dict. If the authentication was successful, you simply redirect the user to the app root “/” or where ever you want.
I tested this, it works like a charm. I also added 2FA successfully, but that would be too much for this post.
Here’s some code (please note: Edge cases and errors are not handled correctly for the sake of simplicity; just a PoC):
Login.razor:
BlazorCookieLoginMiddleware.cs:
and don’t forget to add new middleware to Startup.cs:
This has also been an issue for me. the need to direct to a Razor Page to handle the login as a work around.