Response already started Exception has been thrown on HttpContext.SignOutAsync() method call in Blazor

2.2k views Asked by At

I am trying to use HttpContext.SignOutAsync() in my ASP.NET core Blazor Server application to signout the current user.Exception has been thrown when Httpcontext.SignOutAsync() is called. Does anyone has an idea how to fix this? Thanks in advance. Below are the details of the exception:

Message:

Response already started

Stack Trace:

at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContext.OnStarting(Func2 callback, Object state) at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContext.Microsoft.AspNetCore.Http.Features.IHttpResponseFeature.OnStarting(Func2 callback, Object state) at Microsoft.AspNetCore.Http.DefaultHttpResponse.OnStarting(Func2 callback, Object state) at Microsoft.AspNetCore.Http.HttpResponse.OnStarting(Func1 callback) at Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler.InitializeHandlerAsync() at Microsoft.AspNetCore.Authentication.AuthenticationHandler1.<InitializeAsync>d__42.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.AspNetCore.Authentication.AuthenticationHandlerProvider.<GetHandlerAsync>d__5.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult() at Microsoft.AspNetCore.Authentication.AuthenticationService.d__17.MoveNext() at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at ScrumPortal.Application.Base.Common.ImpersonateUserBase.ImpersonateLogin.d__0.MoveNext() in D:\ScrumPortal\Impersonateuser\scrum-portal\ScrumPortal.Application\Base\Common\ImpersonateUserBase.cs:line 130

Inner Exception :

Null

Startup.cs

           services.AddAuthentication(auth => {
            auth.DefaultScheme = AzureADDefaults.AuthenticationScheme;
            auth.DefaultChallengeScheme = AzureADDefaults.OpenIdScheme;
            auth.DefaultSignInScheme = AzureADDefaults.AuthenticationScheme;
            }).AddAzureAD(options => this.Configuration.Bind("AzureAd", options)).AddCookie(CookieAuthenticationDefaults.AuthenticationScheme,
            options =>
            {
                options.LoginPath = "/signin";
                options.SlidingExpiration = true;
                options.ExpireTimeSpan = new TimeSpan(7, 0, 0, 0);
            });
        services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme,
            options =>
            {
                Configuration.Bind("AzureAd", options);
                options.Events = new OpenIdConnectEvents
                {
                    OnTokenValidated = ctx =>
                    {
                        ClaimsIdentity identity = (ClaimsIdentity)ctx.Principal.Identity;
                        var emailid = identity.Name;
                        var username = identity.Claims.FirstOrDefault(x => x.Type == "name").Value;
                        var res = new LoginUserModel().GetAuthenticatedUserDetails(emailid);
                        if (res != null && res.UserId > 0)
                        {
                            var claims = new LoginUserModel().AddUserClaims(res);
                            identity.AddClaims(claims);
                        }
                        else
                        {
                            ctx.Properties.RedirectUri = "/unauthorized";
                            return Task.FromResult(0);
                        }

                        return Task.FromResult(ctx);
                    }
                };
            });
        services.AddMvc(config =>
        {
            var policy = new AuthorizationPolicyBuilder()
                         .RequireAuthenticatedUser()
                         .Build();
            config.Filters.Add(new AuthorizeFilter(policy));
            config.EnableEndpointRouting = false;
        });

base class

    public partial class ImpersonateLogin : PageModel
    {
        
        public async Task<IActionResult> ImpersonateBtnClick(string impersonateUserId, HttpContext httpcontext)
        {
            string returnUrl = "~/";
            try
            {                    
                string schema = CookieAuthenticationDefaults.AuthenticationScheme;
                await httpcontext.SignOutAsync(schema);
                CommonModel model = new CommonModel();
                int impersonateUser = 0;
                int currentUser = 0;
                int.TryParse(impersonateUserId, out impersonateUser);
                var result = model.GetUserDetailsForImpersonate(impersonateUser);
                if (result != null)
                {
                    bool impersonateUserCheck = (currentUser == impersonateUser) ? false : true;
                    var claims = new System.Collections.Generic.List<Claim>
            {
             new Claim(SessionInfo.RoleId.ToString(), result.RoleId.ToString()),
             new Claim(SessionInfo.EmailId.ToString(), result.EmailId),
             new Claim(SessionInfo.EmployeeName.ToString(), result.DisplayName),
             new Claim(SessionInfo.UserId.ToString(), impersonateUserId.ToString()),
             new Claim(SessionInfo.IsImpersonateUser.ToString(), impersonateUserCheck.ToString().ToLower()),
             new Claim(SessionInfo.CurrentUserId.ToString(), currentUser.ToString()),
             new Claim(SessionInfo.HRRoleId.ToString(), result.HrRoleId.ToString()),
             new Claim(SessionInfo.HRUserId.ToString(), result.HrUserId.ToString()),
            };

                    var claimsIdentity = new ClaimsIdentity(claims, schema);
                    await httpcontext.SignInAsync(schema, new ClaimsPrincipal(claimsIdentity));
                }                   
            }
            catch (Exception ex)
            {

            }

            return LocalRedirect(returnUrl);
        }
    }
1

There are 1 answers

0
Jean-Marc Prieur On

This is by design. Blazor server applications don't run in the context of an HTTP request. your code should not be using the HttpContext. Documentation about it is availble from Threat mitigation guidance for ASP.NET Core Blazor Server | Blazor and shared state.

The right way to sign-out a user in a Blazor server application is to direct the user to an MVC/Razor pages endpoint that takes care of the sign-off.