1

I have two cookies in my browser put there by the IDS4 during sign-in. I need to remove them. Failing to get rid of them by signing out (for whatever reason that may be, despite the docs), I decided to apply a pramatic work-around and remove them manually. It seems those a hard cookie. Two of them...

enter image description here

I attempted to get rid of them like this - targetting all the available schemas as suggested.

await HttpContext.SignOutAsync("Identity.Application");
await HttpContext.SignOutAsync("Identity.External");
await HttpContext.SignOutAsync("Identity.TwoFactorRememberMe");
await HttpContext.SignOutAsync("Identity.TwoFactorUserId");
await HttpContext.SignOutAsync("idsrv");
await HttpContext.SignOutAsync("idsrv.external");

I tried to kill them by explicit hit as proposed here. Apparently, though, that's not how the cookie crumbles.

Response.Cookies.Delete(".AspNetCore.Identity.Application");
Response.Cookies.Delete("idsrv.session");

Nothing of that erases them. They do disappear when I restart the browser, of course, but I need them gone without that measure (also, if I'm to restart the browser, I don't need to log the user out as they will be gone anyway).

I've seen suggestions to call HttpContext.Current but that the same as just simply HttpContext in my controller (according to this). There is talk about Session.Abandon but I don't see that field in my context. There seems to be some issues with this specific matter but I can't tell if those still remain unsolved by the IDS4 team.

edit

public async Task<IActionResult> LogOut([FromQuery] string logoutId)
{
  LogoutRequest context = await InteractionService.GetLogoutContextAsync(logoutId);

  bool? isLoggedIn = User?.Identity.IsAuthenticated;
  isLoggedIn |= User.IsAuthenticated();

  await HttpContext.SignOutAsync();
  await HttpContext.SignOutAsync(IdentityConstants.ApplicationScheme);
  await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);

  //Response.Cookies.Delete("idsrv.session");

  var output = new
  {
    authenticated = isLoggedIn,
    clientId = context.ClientId,
    sessionId = context.SessionId,
    redirect = context.PostLogoutRedirectUri,
    sub = context.SubjectId
  };

  return Ok(output);
  // return SignOut();
}
Konrad Viltersten
  • 36,151
  • 76
  • 250
  • 438
  • What does your entire logout method look like? (the one with SignOutAsync) – Tore Nestenius Sep 26 '21 at 15:48
  • @ToreNestenius I updated the question. The (potentially) interesting part is that when the SPA passes ID token to */endsession* endpoint, I get redirected to this method and obtain a `logoutId`. Exchanging it for the context produces an object with correct ID of the client **but** just `null` for the user (i.e. `sub`), the session and redirect URL. – Konrad Viltersten Sep 27 '21 at 09:36
  • Why do you want to remove these cookies? What's the use case? – abdusco Sep 27 '21 at 11:02
  • @abdusco I've noticed that I got sent to login screen when those cookies are not present (or expired). I can force the application to get to the signin method by clearing the store. I assumed that if I remove them form the code, the same thing would happen. Admittedly, I initially expected them to get removed/invalidated as I call `SignOutAsync` but despite me doign that that (back-channel from *endsession*), the user gets in still. So the original issue is that I can't log the user out. I also noticed that the (logout) `context` has correct `clientId` but `sub` and returnURL are `null`. – Konrad Viltersten Sep 27 '21 at 11:13

2 Answers2

1

Have you tried setting the expire time for the cookie?

Response.Cookies.Append("idsrv.session", "", new CookieOptions() {
    Expires = DateTime.Now.AddDays(-1)
});

When the browser will receive the response from the server, it will identify that the cookie with the name you provided (idsrv.session in this example) has been expired and as a result from this, it will delete the cookie.

Another this that might help is setting the Secure option as true :

"In order to delete a SameSite=None cookie, the replacement cookie with the expiry date in the past also needs to have the Secure flag set. If that is not the case, the cookie won't be deleted (as in: the replacement cookie won't be accepted by Chrome)" -https://www.thinktecture.com/en/identity/samesite/how-to-delete-samesite-cookies/

Response.Cookies.Delete("CookieName", new CookieOptions()
{
   Secure = true,
});
Ran Turner
  • 14,906
  • 5
  • 47
  • 53
  • I'll try that immediately. However, it still begs the question why on Earth doesn't it remove the cookies as it is now. I recall seeing something about *Set-Cookie* in the header of the response setting the `exp` to January 1st '70 since my previous attempts (with deletion, instead of your suggestion), so I'm not hopeful. But I'll definitely give it a try. Got any idea about the not working delete calls in my question? Also, when would you say to append the cookies? I'm not controlling the creation. Just deletion on logout. – Konrad Viltersten Sep 25 '21 at 14:36
  • Hmm, i ran into this article https://www.thinktecture.com/en/identity/samesite/how-to-delete-samesite-cookies/ as well and modified my answer cause i suspect it might be related to the CookieOption Secure , can you give this a try as well? – Ran Turner Sep 25 '21 at 14:42
  • I checked and I see in the headers: `set-cookie: idsrv.session=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/` as well as `set-cookie: idsrv.session=; expires=Fri, 24 Sep 2021 14:41:15 GMT; path=/`. So both deletion and append seem to be appear. But the stupid mini-cake is still present in the browser. It start to feel like I'm missing something fundamental... Just FYI, the expiration date in the browser says *Session* in the column. Not an actual date. I'll try with the `Secure=true`, as well now. – Konrad Viltersten Sep 25 '21 at 14:43
  • Nope. Same result. It says *secure* in the headers, of course. But the actual removal doesn't occur. Is that supposed to happen automatically? I'm asking so because when I try to set a totally new cookie, it doesn't appear in the browser's *Application* tab neither. I only see those two mentioned in my question under *localhost:44304* where my SPA reside (which is not the same as my issuing IDS4 lives). – Konrad Viltersten Sep 25 '21 at 14:52
1

When you do the signout, you should not return anything from the action method, as the SignOutAsync generates its own "response". This is how I do it:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task Logout()
{
    await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
    await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme);

    //Important, this method should never return anything.
}
Tore Nestenius
  • 16,431
  • 5
  • 30
  • 40
  • Does the session cookie disappear before, while or after the invocation of this method? I'm assuming it does disappear at some point, right? I was expecting the little cake to be gone as soon as the `SignOutAsync` is performed (forced to, since it's an asynchronous invokation, of course). But it my case, it's just keeps residing in the cookie store. I'll try the task-no-return version right away. Still, I'm curious what you get in the logout context in that method. I'd like to compare with my stuff. – Konrad Viltersten Sep 27 '21 at 10:02
  • The core is that you need to Sign-out from both the Cookie and OpenID Connect authentication handlers. You should not need to manually delete the cookies. – Tore Nestenius Sep 27 '21 at 10:13
  • I suspect we're running different version/platform because I get significantly different behavior. First of all, POST isn't called at all (breakpoint not hit). Changing it to GET makes the application reach the endpoint but with the same result in the context - client ID is correct, everything else is not there. And, attempting to to go for */connect/authorize* passes by the login method, issuing access immediately (unless I wait 2 minutes which is the lifetime of the session cookie set in *Startup.cs*), upon which, I get the hit on my login point. – Konrad Viltersten Sep 27 '21 at 10:13
  • I get "the core". The issue is that it's not happening as the session cookie stays alive despite running the code. I see some differences (GET vs POST) etc. which makes me sense that we're not talking about the same platform. Are you running SPA or MVC application? – Konrad Viltersten Sep 27 '21 at 10:15
  • Ah, you are also using ASP.NET Core Identity? I have never used that, so it could the the issue.. In my case I am using ASP.NET Core MVC (not SPA) – Tore Nestenius Sep 27 '21 at 10:16
  • What are you using, then? Perhaps I would be able to change in my code? – Konrad Viltersten Sep 27 '21 at 10:17
  • Are you running IdentityServer and the ASP.NET Core client in the same "service"? or separate services? In my client applications I don't use ASP.NET Core Identity as I find it to compliex. Instead I use IdentityServer and ASP.NET Core Client with AddOpenIDConnect/ADdCookie... – Tore Nestenius Sep 27 '21 at 10:18
  • The code in my answer is the signout code for the ASP.NET Core MVC client. It will automatically sign you out from IDentityServer as well. I have never had the need to modify the signout code in IdentityServer. – Tore Nestenius Sep 27 '21 at 10:20
  • @ToreNestenius. This is false. You _can_ return a response from this endpoint. How do you think "redirect to home when logged out" scenario works. `HttpContext.SignOutAsync` just adds a header to add the same cookie in expired state (valid before now), which stops browsers from sending that cookie, and the user is effectively signed out. https://github.com/dotnet/aspnetcore/blob/4b4265972d1155e415633a4a177f159b940cf3bb/src/Shared/ChunkingCookieManager/ChunkingCookieManager.cs#L228-L327 – abdusco Sep 27 '21 at 10:43