Forgotten: Why won’t my ASP.NET site remember me?
We recently ran into an issue with ASP.NET authentication that I thought I would share.
We’re running an ASP.NET MVC 5 web application which uses Microsoft ASP.NET Identity for authentication and authorization. We allow users to choose the built-in “Remember Me” option to allow them to automatically login even if they close the browser. We first set this to expire after 2 weeks, after which they are forced to login again (unless you’re using Sliding Expiration https://msdn.microsoft.com/en-us/library/dn385548(v=vs.113).aspx)
When we first implemented this feature, users would complain that they had to keep logging into the site, despite checking the “Remember Me” option. We first made sure that within the Startup configuration, the CookieAuthenticationOptions.ExpireTimeSpan was explicitly set to 2 weeks (even though that is the default).
After some more troubleshooting and, of course, a stackoverflow hint, we discovered the problem.
What we checked
- We checked to make sure the browser cookie that contains the encrypted authentication token had an expiration date (instead of “Session” or “When the browsing session ends” in Chrome).
In our case the cookie contained the expected expiration date:
- We observed that the “Remember Me” automatic login worked throughout the day, but typically stopped working the next day.
So what happened every night that might trash or invalidate our authentication token? We discovered that the Application Pool for our IIS site was getting recycled nightly.
This alone did not seem to be the culprit, since a user should still be able to automatically login (if they choose to) even if the app pool restarts. Taken a step further, even if the server restarts, this functionality should still work. What about a load-balanced environment where the user doesn’t even hit the same web server? This should all be transparent and the “Remember Me” option should just work.
- We searched the web for scenarios where a user must relogin after a server restart and came across this article: http://stackoverflow.com/questions/29791804/asp-net-identity-2-relogin-after-deploy
This seemed to be the root of the problem – we needed to configure the machinekey settings in order to maintain consistent encryption and decryption keys, even after a server or app pool restart. If this is not set explicitly, new encryption/decryption keys are generated after each restart, which in turn invalidates all outstanding authentication tokens (and forces users to login again).
It turns out that it is easy to generate random keys in IIS, which will set the values directly in your web.config file:
Now we can store the consistent keys in our web.config file and not worry about invalidating a user’s authentication token, even if we restart, redeploy or recycle the app pool on a consistent basis.
In a load-balanced, web farm environment, this setting is critical to allow users to bounce between load-balanced servers with the same authentication token. You just have to make sure that the same encryption/decryption keys are used on each website (configured via the machinekey setting).
If you’re considering using Azure Web Apps, this is transparent since “Azure automatically manages the ASP.NET machineKey for services deployed using IIS”. More details here.
That is a subject for another day.