11
votes

I'm trying to get the Asp.Net Identity login working through Blazor in the Visual Studio template application it still uses Razor Pages and MVC to login, but can only get it to work on the event OnInitAsync, which is not useful because it needs to be done on a button click and not when the page is loading.

My failing code is

protected async Task LoginTest()
{
   await _SignInManager.SignInAsync(new ApplicationUser()
   { UserName = "[email protected]" }, true);
   UriHelper.NavigateTo("/", true);
}

I get the error:

System.InvalidOperationException: The response headers cannot be modified because the response has already started.
at Microsoft.AspNetCore.HttpSys.Internal.HeaderCollection.ThrowIfReadOnly()
at Microsoft.AspNetCore.HttpSys.Internal.HeaderCollection.set_Item(String key, StringValues value)
at Microsoft.AspNetCore.Http.Internal.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 WebApplication3.Pages.Account.Login.RegUser() in C:\Users\david\source\repos\WebApplication3\WebApplication3\Pages\Account\Login.razor:line 28
at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
at Microsoft.AspNetCore.Components.Rendering.Renderer.GetErrorHandledTask(Task taskToHandle)

Has anyone had any success in getting this working? As I mentioned I can get it to work if I put the above function inside the OnInitAsync method but it's no good doing it there.

Any help would be much appreciated.

2
Did you ever figure this out? - Robert Swilley
No since I found this GitHub issue github.com/aspnet/AspNetCore/issues/11411, I ended up creating a work-around solution using the post-redirect-get approach. In the post I encrypted a login token with the user details and passed it to another page as a get with the token in the parameter, then I read the encrypted token and login the user. - David Hawkins
Thanks for the link. I was trying to use _httpContextAccessor.HttpContext.SignInAsync in a Razor Component's ViewModel. I swear I had it working correctly, but after some refactoring, I couldn't get it to work. I tried to revert and it wasn't working. It was a late-night... Then I realized it is supposed to done in a Razor Page, not a Razor Component. - Robert Swilley
@DavidHawkins plase can yot tell me how did you encrypt the login information to pass in get call? thanks - white.devils
It seems you cannot write anything to the headers after the Blazor app is up (like cookies). This I think is because there is no request/response. The only response is the response that served the initial page. Writing cookies should be done using javascript interop - but this should fix the RefreshSignInAsync because it is simply a bug. - Cesar

2 Answers

2
votes

After reading David Hawkins post and some digging, I found a workaround solution as he described on https://github.com/dotnet/aspnetcore/issues/13601#issuecomment-679870698. Simple and effective.

In the proposed solution, there is no encryption of the user/password details in the middleware, as it stays on the server.

As of now (April 2021) it doesn't seems if ASP.Net Core 5 and Identity supports Blazor calling SignInManager's *SignIn methods. By the time it is called, the HTTP headers have already been send and cannot be modified/appended too.

-2
votes

I found this related GitHub issue http://github.com/aspnet/AspNetCore/issues/11411

I ended up creating a work-around solution using the post-redirect-get approach. In the post I encrypted a login token with the user details and passed it to another page as a get with the token in the querystring, then I read the encrypted token and login the user.