Jamadan
Jamadan

Reputation: 2313

MVC 5 authenticated user being logged out, no "remember me" (ExpireTimeSpan / MachineKey / Session Timeout)

Preface: I've read this (ASP.NET MVC membership - user being logged out frequently - don't know why) and this (ASP.NET Identity 2 Remember Me - User Is Being Logged Out) and this (ASP.NET 5 Identity 3 users get signed out after some time) and this (User gets logged out with 'Remember me').

I'm having a bit of a nightmare here. I set up a boiler plate MVC5 app in VS 15. Everything has been updated to latest (in particular identity.core and identity.owin as I read there was a problem with boiler plate MS stuff).

We noticed that users were being logged out after only roughly 10-15 minutes of inactivity on one of our apps. Also our users were reporting that the "remember me" function simply didn't work.

I can't work out what I'm doing wrong tho...

Startup.Auth.cs:

app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            SlidingExpiration = true,
            ExpireTimeSpan = System.TimeSpan.FromDays(30.0),
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Account/Login"),
            Provider = new CookieAuthenticationProvider
            {
                // Enables the application to validate the security stamp when the user logs in.
                // This is a security feature which is used when you change a password or add an external login to your account.  
                OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                    validateInterval: TimeSpan.FromMinutes(30),
                    regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
            }
        });

My config file:

<system.web>
    <httpCookies httpOnlyCookies="true" requireSSL="true" lockItem="true" />
    <authentication mode="Forms">
      <forms loginUrl="~/Account/Login" slidingExpiration="true" timeout="60" requireSSL="true" />
    </authentication>
    <compilation targetFramework="4.5.2" />
    <httpRuntime targetFramework="4.5.2" />
    <machineKey validationKey="AutoGenerate,IsolateApps" decryptionKey="AutoGenerate,IsolateApps" validation="SHA1" decryption="Auto" />
    <httpModules>
      <add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" />
    </httpModules>
</system.web>

Authentication in AccountController:

    [ValidateAntiForgeryToken]
    [HttpPost]
    [AllowAnonymous]
    public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }

        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, change to shouldLockout: true
        var result = await SignInManager.PasswordSignInAsync(model.Username, model.Password, isPersistent: true, shouldLockout: false);
        switch (result)
        {
            case SignInStatus.Success:
                return RedirectToLocal(returnUrl);
            case SignInStatus.LockedOut:
                return View("Lockout");
            case SignInStatus.RequiresVerification:
                return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
            case SignInStatus.Failure:
            default:
                ModelState.AddModelError("", "Invalid login attempt.");
                return View(model);
        }
    }

I'm basically trying to give the user a 30 sliding cookie. i.e. as long as they visit the site within 30 days and haven't cleared their cookies they won't have to re-authenticate.

As you can see, isPersistent is set to true on log in, so users should effectively always be treated as if rememberMe = true;

Is there something obvious I have missed here, or am I completely not understanding how isPersistent and perpetual login works?

If I've done something stupid please feel free to heckle. Thanks in advance.

NB: I've modified the timeout value on the forms authentication entry in the config file, no changes from what I can see from testing.

EDIT: Ive spoken to my hosting company, they were recycling the app pool after 15 minutes of inactivity. They have extended that now. However, that doesn;t get past the problem.

I thought having the machine key would ensure the cookie would survive and app recycle? I've put an explicit machine key in now and seeing how it goes. Still a bit stumped as to the cause of the problem and how to address it...

Upvotes: 4

Views: 1716

Answers (1)

Jamadan
Jamadan

Reputation: 2313

So for some reason (still not sure what) my fiddling seems to have worked.

I ended up having to generate an explicit Machine key and not use

validationKey="AutoGenerate,IsolateApps" 
decryptionKey="AutoGenerate,IsolateApps"

I also asked my hosting company to extend the IIS app pool idle recycle time (not sure that's the correct name but that's what the hosting support guy called it).

This seems to have solved it and testing over night I was not logged out. Still a little bit stumped. The app pool is still recycling according to the hosting company, albeit at a larger interval.

I'm now thinking the explicit machine key has something to do with it maybe? But I've used "AutoGenerate,IsolateApps" before and it has worked fine.

I hope this helps someone else. Don't really have time yet to do full testing and remove the machine key to see if it still works. I will report back once I can set up some definitive testing.

Upvotes: 3

Related Questions