35

I am working on a logout feature in the application we are using ASP.NET Identity login. I can login successfully but when I logout and then try to login again I get the following message:

The provided anti-forgery token was meant for a different claims-based user than the current user.

Here is my logout code:

 public ActionResult Logout()
        {
            SignInManager.Logout();
            return View("Index"); 
        }

**SignInManager.cs**
 public void Logout()
        {
            AuthenticationManager.SignOut(); 
        }

After the user press the logout button he is taken to the login screen. The url still says "http://localhost:8544/Login/Logout". Since we are on the login screen maybe it should just say "http://localhost:8544/Login".

john doe
  • 9,220
  • 23
  • 91
  • 167
  • 1
    I think you can make it work using a redirect in your logout action, instead of returning the view directly. `return RedirectToAction("Index");` That will hopefully set all headers and cookie info right. – Silvermind Mar 25 '16 at 16:56
  • In my case it was some issue with EDMX file. Once I updated all tables the problem gone. – NoWar Nov 07 '18 at 08:07

7 Answers7

22

What worked for me was switching the order of the middlewares used. Add first app.UseAuthentication() and then the antiforgery stuff. This is how I did it:

app.UseAuthentication();
app.Use(next => ctx =>
        {
            var tokens = antiforgery.GetAndStoreTokens(ctx);

            ctx.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken,
                new CookieOptions() { HttpOnly = false });

            return next(ctx);
});

Doing it the other way around creates a token that is not meant for authenticated users.

Arturo Moreno
  • 281
  • 4
  • 4
18

You are returning a View, rather than calling RedirectToAction(). So what is happening is the view is running under the context of the logout request, where the user is still logged in. They won't be logged out until the request finishes.

So, try

public ActionResult Logout()
{
    SignInManager.Logout();
    return RedirectToAction("Index", "Home");
}
blowdart
  • 55,577
  • 12
  • 114
  • 149
12

I found that users were experiencing this issue when they would submit the login page when already authenticated. I replicated this error by:

  1. Opening two tabs when logged in,
  2. Logging out from one,
  3. Reloading both,
  4. Logging in to one,
  5. Trying to log in with the other. The error occurred before entry to the POST: /Account/Login action.

The majority of my users use the web app on a mobile device, so it made sense that they had bookmarked the login page and pulled it up and submitted when they had a tab opened in the background already logged in. I also surmised that sometimes they would have a dormant tab loaded with the login form and just pull that tab up and submit.

I realize that there are many ways to solve this issue. I solved this with two changes:

  1. I added a check on User.Identity.IsAuthenticated to my "GET: /Account/Login" action:
if (User.Identity.IsAuthenticated)
{
   try
   {
      return RedirectToLocal(returnUrl);
   }
   catch
   {
      return RedirectToAction("index", "Home");
   }
}
  1. In my controller I created a "check if logged in" action:
[AllowAnonymous]
public JsonResult CheckLogedIn()
{
    try
    {
        return Json(new { logged_in = User.Identity.IsAuthenticated }, JsonRequestBehavior.AllowGet);
    }
    catch
    {
        return Json(new { logged_in = false }, JsonRequestBehavior.AllowGet);
    }
}

And I called it repeatedly in the view to redirect all open login forms away from the login page when already logged in:

<script type="text/javascript">
    setInterval(function () {
        $.ajax({
                url: '@Url.Action("CheckLogedIn", "Account")',
                type: "GET",
            }).done(function (data) {
                if (data.logged_in) {
                    window.location = '/';
                }
            });
    }, 5000);  
</script>

This worked well for me. Hope it helps you.

Justin
  • 474
  • 4
  • 14
  • 1
    Thank you for the explanation! I see this error from times to times in our logs but wasn't able to reproduce it, yet. Thanks to your answer I'm now able to do so - and will probably fix that error :-) – Jan Köhler Nov 06 '20 at 08:32
  • 1
    Thank you. I have had this for a few years and couldn't figure it out. This solved it. – Stackedup Sep 09 '22 at 02:59
  • If I was to need to revise this process, I would try the Broadcast Channel API: https://www.smashingmagazine.com/2022/09/javascript-api-guide/#broadcast-channel-api – Justin Dec 19 '22 at 21:00
11

Try this:

public ActionResult Logout()
{
    AuthenticationManager.SignOut();
    Session.Abandon();
    return RedirectToAction("Index");
}

That will reload your login page which will provide you a new CSRF token.

univ
  • 717
  • 4
  • 12
  • 7
    asp.net auth isn't linked to session in any way :) – blowdart Mar 25 '16 at 17:31
  • 3
    @blowdart True, but depending on the application, it may make sense to purge session variables, especially if associated with that user's session, at the same time the user is logged out. – Mark1270287 Nov 26 '18 at 15:35
10

I've been getting this same error on the login for a LONG time now, but haven't been able to work out why. Finally I found it, so I'm posting it here (although it's a slightly different cause) in case someone else has it.

This was my code:

//
// GET: /login
[OutputCache(NoStore = true, Location = System.Web.UI.OutputCacheLocation.None)]
public ActionResult Login()
{
    return View();
}

//
// POST: /login
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
    AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);

    if (!ModelState.IsValid)
    {
        return View(model);
    }
    //etc...

This worked fine for 99.99% of the logins, but every now & then I got the above-mentioned error, although I couldn't reproduce it, until now.

The error only happens when someone clicks the login button twice in quick succession. However, if I remove the AuthenticationManager.SignOut line in the Login action, then it's fine. I'm not sure why I put that line in there, but it's causing the issue - and removing it fixes the problem.

Sean
  • 14,359
  • 13
  • 74
  • 124
1

I didn't have the AuthenticationManager.SignOut command as Sean mentioned in my Login method. I was able to reproduce by clicking on the login button more than once before hte next View loads. I disabled the Login button after the first click to prevent the error.

<button type="submit" onclick="this.disabled=true;this.form.submit();"/>
sonicbabbler
  • 821
  • 1
  • 12
  • 19
-3

Try this:

 public ActionResult Login(string modelState = null)
    {
        if (modelState != null)
            ModelState.AddModelError("", modelState );

        return View();
    }
 [ValidateAntiForgeryToken]
    public async Task<ActionResult> Login(LoginViewModel model)
    {
        AuthenticationManager.SignOut();

        return RedirectToAction("Login", "Controller", new { modelState = "MSG_USER_NOT_CONFIRMED" });
    }
  • 3
    Welcome to Stack Overflow! Please don't answer just with source code. Try to provide a nice description about how your solution works. See: [How do I write a good answer?](https://stackoverflow.com/help/how-to-answer). Thanks – sɐunıɔןɐqɐp Aug 17 '18 at 18:34