jeudi 22 septembre 2016

ASP.NET MVC3 Forms Authentication Expiring Prematurely

We have an online assessment platform built with MVC3. It runs in IIS on a single dedicated server running Windows Server 2012 R2 over which we have full control. Recently users have been reporting that they are "getting logged out" during assessments.This is a problem as many of our assessments have time constraints and logging back in costs users valuable seconds.

I have been unable to replicate the issue during testing, but have confirmed by consulting our logs that for the last 2 months ~15-20% of users have to log back in at some point during their assessment. For the 10 months prior to that only <2% had to log back in.

I have compared our current code base to how it was 3 months ago, and nothing that is even remotely related to logging in and authentication has been changed. To my knowledge no settings on the server have been changed.

There are many hundreds of files, and thousands of lines of code in this application, but I will try and share the relevant bits in the hope that someone can help me resolve this. If there is any information that I have missed, leave a comment and I will add it as soon as possible.

From Web.config:

<authentication mode="Forms">
  <forms loginUrl="~/Login/" timeout="300" slidingExpiration="true" />
</authentication>

This is how we create the auth cookie:

Guid User_Id = /* lookup id with username after verification */
string User_Role = /* CSV specifying the roles the user has */
DateTime Expiry = DateTime.Now.AddHours(5);
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(1, User_Id.ToString(), DateTime.Now, Expiry, false, User_Role, "/");
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(authTicket));
cookie.Expires = Expiry; 
HttpContext.Current.Response.Cookies.Add(cookie);

We user a custom implementation of the AuthorizeAttribute to restrict access to most actions like this:

public class MyController : Controller
{
    [CustomAuthorize(Roles = "MyRole")]
    public ActionResult MyAction()
    {
        // do some stuff
        return View();
    }
}

Which defined as follows:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        string cookieName = FormsAuthentication.FormsCookieName;

        if (!filterContext.HttpContext.User.Identity.IsAuthenticated ||
            filterContext.HttpContext.Request.Cookies == null ||
            filterContext.HttpContext.Request.Cookies[cookieName] == null
        )
        {
            HandleUnauthorizedRequest(filterContext);
            return;
        }

        var authCookie = filterContext.HttpContext.Request.Cookies[cookieName];
        var authTicket = FormsAuthentication.Decrypt(authCookie.Value);
        string[] roles = authTicket.UserData.Split(',');

        var userIdentity = new GenericIdentity(authTicket.Name);
        var userPrincipal = new GenericPrincipal(userIdentity, roles);

        filterContext.HttpContext.User = userPrincipal;
        base.OnAuthorization(filterContext);
    }
}

This has been the setup for the last 3 years, and it has only been the last 2 months that have been problematic. As you can see the cookie is set to expire 5 hours after its creation. Users have been typically pretty vague with their reports, and looking at our logs the amount of time between initial login and having to login again ranges from anywhere between a few minutes and a couple of hours. Even so I have had a look on the server at what I think are the relevant settings and can't see any thing that may cause a problem:

IIS Settings

If any one has any ideas at all I would love to hear them.

Aucun commentaire:

Enregistrer un commentaire