11

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 = "test@test.com" }, 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.

David Hawkins
  • 1,049
  • 1
  • 10
  • 30
  • Did you ever figure this out? – Robert Swilley Jul 10 '19 at 01:05
  • 1
    No since I found this GitHub issue https://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 Jul 10 '19 at 22:07
  • 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 Jul 11 '19 at 13:52
  • @DavidHawkins plase can yot tell me how did you encrypt the login information to pass in get call? thanks – white.devils Oct 07 '19 at 20:56
  • 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 Oct 20 '19 at 17:29
  • @DavidHawkins Please can you share the code you mentioned in your comment. I tried to implement this, but had the same exception even when calling `_SignInManager.SignInAsync` inside `OnInitializedAsync` (I assume that's the same as what you mean by `OnInitAsync` as I don't see a method with that name to override). Thanks – Avrohom Yisroel Dec 20 '20 at 17:48

2 Answers2

2

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.

JJ-za
  • 226
  • 2
  • 6
  • Thanks. I raised this issue, just on the offchance that it happens. https://github.com/dotnet/aspnetcore/issues/35551 – Webreaper Aug 20 '21 at 15:36
  • 1
    @Webreaper, I agree with your issue raised and I'm glad it is not just me who have this issue. It got worse for me as my project moved from Blazor Server to Blazor WASM. I eventually build all of the Identity Server UI MVC into Blazor components and with a lot of middleware code manage to implement a seamless UI and handle auth redirects in Blazor with backend API calls to speak to Identity Server (not a nice experience). – JJ-za Aug 21 '21 at 16:29
  • There might be a potential workaround for this - see this Q: https://stackoverflow.com/questions/68889811/can-we-consume-a-blazor-component-as-a-web-component-within-a-regular-non-blazor and this article by the inimitable Mr Sainty: https://chrissainty.com/using-blazor-components-in-an-existing-mvc-application/ – Webreaper Aug 23 '21 at 10:58
  • Unfortunately it not works in Firefox (in Chrome, Opera, Edge - Ok). Anybody, please, do you know universal (including Firefox) solution for Blazor Server? – Lealan Nov 21 '21 at 15:08
  • @JJ-za everything related to Identity like SignIn, Login, .. should stay in MVC as it is the most secure way to handle it. – MarchalPT Dec 15 '22 at 11:44
  • @MarchalPT, MVC is a UI framework and the same can be achieved, and as secure, using REST API. Blazor doesn't necessary means the authentication takes place client side, and once you have a JWT token in the client there is no difference regardless what UI framework you are using. – JJ-za Dec 19 '22 at 07:29
-2

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.

David Hawkins
  • 1,049
  • 1
  • 10
  • 30