Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HttpContext.SignOutAsync() not deleting local cookie

I am following along the IdentityServer4 Quickstart project to get up to speed on how it is implemented. My Startup.cs is close to identical, my implementation of the AccountController.cs class is more or less the same, and so on.

However, there seems to be some sort of low-level conflict with how local authentication cookies are handled.

  1. In the startup, I do not call app.UseAuthentication() as IdentityServer sets that up later on. I have gone so far as to copy the services.AddAuthentication() from the quickstart project verbatim, but am still having issues.

When logging in, the Quickstart project produces two cookies: one Antiforgery Validation cookie, and one cookie called idsrv. Nowhere in the project is this explicitly defined to do so.

When I run my implementation of it, however, I get three cookies: An Antiforgery Validation cookie, an idsrv.session cookie, and an .AspNetCore.Identity.Application cookie. I can force ASP.NET to name the cookie 'idsrv', but the existence of a .session cookie leads me to believe it isn't using the right scheme.

  1. When I try to log out by calling HttpContext.SignOutAsync(), the cookie is not deleted and nothing happens: it stays signed in. I have found questions with similar problems, but those seem to be a) implementing an external auth and b) implementing redirects that supposedly overwrite the signout url. I am doing neither.

My login implementation:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login( LoginInputModel model, string action )
{
   var context = await _interactionService.GetAuthorizationContextAsync( model.ReturnUrl );

   if( action != "Login" )
   {
      if( context != null )
      {
         // Handle a cancelled login
         await _interactionService.GrantConsentAsync( context, ConsentResponse.Denied );
      }
      else
         return Redirect( "~/" );
   }

   var errors = ModelState.Values.SelectMany( v => v.Errors );
   if( ModelState.IsValid )
   {
      var user = await _userManager.FindByNameAsync( model.Username );
      if( user != null && await _userManager.CheckPasswordAsync( user, model.Password ) )
      {
         await _eventService.RaiseAsync( new UserLoginSuccessEvent( user.UserName, user.Id, user.UserName ) );

         //Handle RememberMe Checkbox
         AuthenticationProperties props = null;
         if( model.RememberMe )
         {
            props = new AuthenticationProperties
            {
               IsPersistent = true,
               ExpiresUtc = DateTimeOffset.UtcNow.Add( Config.AccountRememberMeDuration ),
            };
         }

         //Issue Auth Cookie
         await HttpContext.SignInAsync( user.Id, user.UserName, props );

         if( _interactionService.IsValidReturnUrl( model.ReturnUrl ) || Url.IsLocalUrl( model.ReturnUrl ) )
            return Redirect( model.ReturnUrl );
         return Redirect( "~/" );
      }

      //If we made it this far, the authentication failed
      await _eventService.RaiseAsync( new UserLoginFailureEvent( model.Username, "Invalid Credentials" ) );
      ModelState.AddModelError( "", "Invalid Credentials" );
   }

   //Display form with error message
   model.Password = string.Empty;
   return View( model );
}

My Logout Implementation:

[HttpGet]
public async Task Logout( LogoutInputModel model )
{
   if( User?.Identity.IsAuthenticated == true )
   {
      await HttpContext.SignOutAsync();
      await _eventService.RaiseAsync( new UserLogoutSuccessEvent( User.GetSubjectId(), User.GetDisplayName() ) );
   }
}

My Startup.cs:

public void ConfigureServices( IServiceCollection services )
{
   services.AddMvc();
   services.AddIdentityServer()
      .AddOperationalStore( options =>
      {
         options.ConfigureDbContext = builder =>
             builder.UseSqlServer( Config.ConnectionString, sqlOptions => sqlOptions.MigrationsAssembly( Config.MigrationsAssembly ) );

         options.EnableTokenCleanup = true;
         options.TokenCleanupInterval = 30; //Every 30 seconds
      } )
      .AddConfigurationStore( options =>
      {
         options.ConfigureDbContext = builder =>
             builder.UseSqlServer( Config.ConnectionString, sqlOptions => sqlOptions.MigrationsAssembly( Config.MigrationsAssembly ) );
      } )
      .AddDeveloperSigningCredential();

   services.AddDbContext<CustomUserContext>( builder =>
      builder.UseSqlServer( Config.ConnectionString, sqlOptions => sqlOptions.MigrationsAssembly( Config.MigrationsAssembly ) )
   );
   services.AddIdentity<CustomUser, CustomRole>()
      .AddEntityFrameworkStores<CustomUserContext>();

   services.AddAuthentication();
}

public void Configure( IApplicationBuilder app, IHostingEnvironment env )
{
   if( env.IsDevelopment() )
      app.UseDeveloperExceptionPage();

      
   app.UseIdentityServer();
   app.UseStaticFiles();
   app.UseMvcWithDefaultRoute(); //TODO: Routes

}

How I'm calling the logout function:

<div class="account-actions">
    @using( Html.BeginForm( "Logout", "Account", FormMethod.Get ) )
    {
        <input type="submit"
               class="account-action-button account-action-logout"
               value="Logout" />
    }
</div>
like image 238
w0f Avatar asked Oct 28 '25 07:10

w0f


2 Answers

I had the same issue. I thought I might try to call SignOutAsync( schema ) instead. By trying to sign out of a schema that didn't exist I got an error message with the list of schemas supported. One of them was "Identity.Application" and doing a sign out on that worked. e.g. await this.HttpContext.SignOutAsync( "Identity.Application" );

like image 191
Mike Bennett Avatar answered Oct 29 '25 20:10

Mike Bennett


As far as I can see you are cohosting the IdentityServer within your MVC app, and I believe you need to configure Cookie middleware for your MVC app. Configuration you have right now in Startup.cs is for IdentityServer itself. And the cookie you get is the one for the IdentityServer.

Take a look at how you configure cookie authentication + OIDC in this sample: https://github.com/IdentityServer/IdentityServer4.Samples/blob/release/Clients/src/MvcHybrid/Startup.cs

like image 44
bychkov Avatar answered Oct 29 '25 21:10

bychkov



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!