1

I am using Blazor server side and want to set Cookie for that i am using SignInAsync function of HttpContext but giving me this error "The response headers cannot be modified because the response has already started".

The error flashes when it comes to the following line

await httpContextAccessor.HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, 
   new ClaimsPrincipal(claimsIdentity), 
    authProperties);

I have tried every this but still facing the error

My Startup.cs Page


    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using power_SLIC.Data;
    using System.Net.Http;
    using power_SLIC.Services;
    using Microsoft.AspNetCore.Components.Authorization;
    using Microsoft.AspNetCore.Authentication.Cookies;
    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.DependencyInjection.Extensions;
    
    namespace power_SLIC
    {
        public class Startup
        {
            public Startup(IConfiguration configuration)
            {
                Configuration = configuration;
            }
    
            public IConfiguration Configuration { get; }
    
            // This method gets called by the runtime. Use this method to add services to the container.
            // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddRazorPages();
                services.AddServerSideBlazor();
                services.AddSingleton<WeatherForecastService>();
                // services.AddProtectedBrowserStorage();
                services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    
                services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
                    .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme,
                        options => {
                            options.LoginPath = "/";
                        });
                //.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, act => {
                //    act.LoginPath = "/";
                //    act.AccessDeniedPath = "/";
                //    act.SlidingExpiration = true;
                //}
    
                // services.AddHttpClient();
                services.AddSingleton<HttpClient>();
              //  services.AddScoped<AuthenticationStateProvider, TokenAuthenticationServices>();
                //services.AddScoped<ILoginservices, TokenAuthenticationServices>();
               /// services.AddSingleton<HttpClient>();
                services.AddAuthorization(option =>
                {
                    option.AddPolicy("Employee", policy => policy.RequireClaim("IsUserEmployee", "true"));
                });
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
                else
                {
                    app.UseExceptionHandler("/Error");
                    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                    app.UseHsts();
                }
    
                app.UseHttpsRedirection();
                app.UseStaticFiles();
                app.UseRouting();
                app.UseAuthentication();
                app.UseAuthorization();
    
              
    
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapBlazorHub();
                    endpoints.MapFallbackToPage("/_Host");
                });
            }
        }
    }

And My Razor Page looks like this Loginscreen.razor

@layout LoginLayout
@page "/"

@using System.Threading.Tasks
@using Microsoft.AspNetCore.Authentication
@using Microsoft.AspNetCore.Authentication.Cookies
@using Microsoft.AspNetCore.Http
@using Microsoft.AspNetCore.Mvc
@using Newtonsoft.Json
@using System.Text
@using Newtonsoft.Json.Linq
@using Microsoft.AspNetCore.ProtectedBrowserStorage
@using System.Security.Claims
@using System.Web
@*@inject ProtectedSessionStorage ProtectedSessionStore*@
@inject IJSRuntime JsRuntime;
@inject NavigationManager NavigationManager
@inject HttpClient http
@inject IHttpContextAccessor httpContextAccessor



<div style="margin-left:39%;margin-right:17%"> 
<div class="card">
    <h4 class="card-header">Login</h4>
    <div class="card-body">
        <EditForm Model="@model" >
            @*<DataAnnotationsValidator />*@
            <div class="form-group">
                <label>Username</label>
                <InputText @bind-Value="model.SSam_User" class="form-control" />
                @*<ValidationMessage For="@(() => model.Username)" />*@
            </div>
            <div class="form-group">
                <label>Password</label>
                <InputText @bind-Value="model.SSam_Pwd" type="password" class="form-control" />
                @*<ValidationMessage For="@(() => model.Password)" />*@
            </div>
            <button  class="btn btn-primary" @onclick = "AddItem" >
               
                Login
            </button>
           @* <NavLink href="account/register" @onClick = "window.location.href = 'home'" class="btn btn-link">Register</NavLink>*@
        </EditForm>
    </div>
</div>
</div>


@code{
    private bool isConnected = false;
    private Model.LoginModel model = new Model.LoginModel();
    class Values
    {
       
        public string result {get; set;}

    }


         




    class Result{
        public List<Values>  result { get; set;}
    };

   public async void AddItem()
    {
        var addItem = new Model.LoginModel {SSam_User=model.SSam_User,SSam_Pwd=model.SSam_Pwd, UnitSname = "HFHO" };
        string output = JsonConvert.SerializeObject(addItem);
        var Stringcontent = new StringContent(output,Encoding.UTF8,"application/json");
        
        var op = await http.PostAsync("https://localhost:44392/api/outputs/getpowerbi_token", Stringcontent);
         var resultcontent = op.Content.ReadAsStringAsync().Result;
            
       dynamic oit = JsonConvert.DeserializeObject<dynamic>(resultcontent);

        var accesstoken = oit["result"]["Token_status"].Value;
        var op1 = oit["result"]["login_response"][0]["status"].Value;

        if(op1 == "success")
        {
            var reo = httpContextAccessor.HttpContext.Request.Headers;
            var prerender = !httpContextAccessor.HttpContext.Response.HasStarted;  

             if (!httpContextAccessor.HttpContext.Request.Headers.ContainsKey("Header"))
             {
                 if (!httpContextAccessor.HttpContext.Response.HasStarted)
                 {
                     string result;
                     httpContextAccessor.HttpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
                     result = JsonConvert.SerializeObject(new { error = "Request doesn't contains header" });
                     httpContextAccessor.HttpContext.Response.ContentType = "application/json";
                     await httpContextAccessor.HttpContext.Response.WriteAsync(result);
                 }
                 else
                 {
                     await httpContextAccessor.HttpContext.Response.HttpContext.Response.WriteAsync(string.Empty);
                 }
                 var reo1 = httpContextAccessor.HttpContext.Response.HttpContext.Response;
    
                 AuthenticateResult.Fail("request doesn't contains header");
             }

          
            var claims = new List<Claim>
{
    new Claim(ClaimTypes.Name, model.SSam_User),
    new Claim("Token", accesstoken),
    new Claim(ClaimTypes.Role, "User"),
};



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

            var authProperties = new AuthenticationProperties
            {
                IsPersistent = true,
                RedirectUri = "/",
                ExpiresUtc = DateTime.UtcNow.AddSeconds(30)
            };
            var j1 = CookieAuthenticationDefaults.AuthenticationScheme;

            
       await httpContextAccessor.HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, 
   new ClaimsPrincipal(claimsIdentity), 
    authProperties);

     NavigationManager.NavigateTo("/powerbi",true);

        }

      

    }
}
    
Ritesh Dange
  • 29
  • 1
  • 3
  • _"I have tried every(thing)"_, what in particular? – abdusco Jul 06 '21 at 07:58
  • @abdusco I tried whether it is prerendering and also tried making response empty.. Please help !! – Ritesh Dange Jul 06 '21 at 08:02
  • The error flases when it come to the line " await httpContextAccessor.HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity), authProperties);" – Ritesh Dange Jul 06 '21 at 08:17
  • This is not going to work. Cookies are set on a HTTP Server. You try to do it in a SignalR client. – H H Jul 06 '21 at 08:44
  • The error is pretty clear -you can't modify headers once you start sending a response. This has nothing at all to do with prerendering and trying to *modify* the response by "making it empty" actually means that something was already sent. – Panagiotis Kanavos Jul 06 '21 at 10:13
  • @PanagiotisKanavos Can you please help how can i find out was is getting already sent...... – Ritesh Dange Jul 06 '21 at 10:17

1 Answers1

2

The problem is async void. This is only meant for asynchronous event handlers, which aren't used in Blazor. Such methods can't be awaited, which means the caller won't start and wait for them to complete. Since the runtime has no idea that AddItem is still running, it starts sending the response to the client.

Asynchronous methods that have no result should return async Task, not async void :

public async Task AddItem()
{
...
}
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236