2

I'm having this issue with my current session manager, when the session expires it will not log off the user.

At random intervals while user is logged in the HttpContext.Current is NULL causing the site to generate many errors. I've tried a couple of techniques to redirect user to login upon session expiration, with no success.

  1. Under Global.asax I'm making use of Session_End can attempted to call LogOut method but this gets executed even before user logs in.
  2. Under Global.asax I added Application_AcquireRequestState, but sadly this makes way to many calls to the SSO Service. Also, it never redirected to login upon session expiration. I tested it with the following FormsAuthentication.RedirectToLoginPage(); No luck.

I found this answer by Lex Li "Why HttpContext.Current be null?" - it gave me some insight on what my problem could be, even-though my application doesn't make use of background-threads, my HttpContext.Current comes back Null with some random requests, doesn't always happen.

Session Manager

   public class SessionManager
    {
        private const string SessionKey = "AppSession";

        private SessionManager()
        {
            GUID                  = new Guid();
            FirstName             = String.Empty;
            LastName              = String.Empty;
            Email                 = String.Empty;
            SessionExpiration     = new DateTime();
        }

        // Gets the current session.
        public static SessionManager Current
        {
            get
            {
                if(HttpContext.Current != null)
                {
                    if (HttpContext.Current.Session[SessionKey] == null)
                    {
                        var model = new SessionManager();
                        HttpContext.Current.Session[SessionKey] = model;
                    }
                    return (SessionManager)HttpContext.Current.Session[SessionKey];
                }
                else
                {
                    return null;
                }
            }

        }

        public static void LogOffUser()
        {
            //SSO is a seperate service, I need to stay insync.
            var ssoAuth = new SSOAuth(); 
            if(Current != null)
               ssoAuth.SSOLogoffUser(Current.GUID);

            FormsAuthentication.SignOut();  
            FormsAuthentication.RedirectToLoginPage();
        }

        public Guid GUID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public DateTime SessionExpiration { get; set; }
    }

Login Controller

[HttpPost]
[AllowAnonymous]
public JsonResult Login(LoginViewModel user)
{
    var ssoAuth = new SSOAuth();
    var sessionObject = ssoAuth.SSOLoginUser(user.Email, user.Password);

    if (sessionObject != null && sessionObject.SessionId != Guid.Empty)
    {
        SessionManager.Current.Email                 = sessionObject.UserId;
        SessionManager.Current.GUID                  = sessionObject.SessionId;
        SessionManager.Current.FirstName             = sessionObject.FirstName;
        SessionManager.Current.LastName              = sessionObject.LastName;
        SessionManager.Current.SessionExpiration     = sessionObject.SessionExpiration;

        //Grab difference in time to get session timeout in minutes.
        var differenceInTime = SessionManager.Current.SessionExpiration - DateTime.Now;
        Session.Timeout = differenceInTime.Minutes;

        //Authenticate user
        FormsAuthentication.SetAuthCookie(user.Email, false);

        //We return JSON, Its an AJAX Call.
        return Json(new
        {
            redirectUrl = "/Home.mvc/Home/Index",
            isRedirect = true
        });
    }

    return Json(new
    {
        redirectUrl = string.Empty,
        isRedirect = false
    });
}
Community
  • 1
  • 1
Dayan
  • 7,634
  • 11
  • 49
  • 76
  • HttpContext.Current should never be null, except in Application_Start(), if it is, you must have something that is setting it to null somewhere. This is just not a normal situation. If I were you, I would do a global search for HttpContext and visually inspect each location to make sure you are not using a single = rather than a == somewhere. – Erik Funkenbusch Jul 21 '14 at 15:48
  • 1
    You also should never use session for authentication either. Session is not secure. Plus if your app pool is recycled, you loose your session, but your auth cookie will still be valid so things will be out of sync. – Erik Funkenbusch Jul 21 '14 at 15:53
  • @ErikFunkenbusch Thank you for your points, I'm doing a global search now and I can't find anything that sets `HttpContext`, its being used but not changed. Also what would be your recommendation on authentication, I was storing user information in session to ensure its used globally and in sync with the web service. – Dayan Jul 21 '14 at 16:00
  • Well, I would store most of that information in the User data section of the Auth Cookie. You already have the userid in the auth cookie so that doesn't have to be saved, you can set the expiration of the cookie to the same as the SessionExpiration so that doesn't need to be saved, that just leaves FirstName, LastName and Guid. See http://www.danharman.net/2011/07/07/storing-custom-data-in-forms-authentication-tickets/ – Erik Funkenbusch Jul 21 '14 at 16:46
  • @ErikFunkenbusch I'm going over this article, Thank you! Please, why not post it as an answer and if things go well with my implementation from that article I can accept your answer. – Dayan Jul 21 '14 at 17:22
  • Well, that doesn't solve your problem with a null HttpContext.Current, and unfortunately there's just not enough information here to solve that. – Erik Funkenbusch Jul 21 '14 at 18:12
  • @ErikFunkenbusch What else can I provide to help you assist me better with this? – Dayan Jul 21 '14 at 19:01
  • I don't see `LogOffUser()` being called, and I don't see that method clear out your session so it's possible to be logged off, but the session is still full of user data. – Erik Philips Jul 24 '14 at 20:54
  • Are you sure that your `HttpContext.Current` is `NULL`? I can only think of a case where `HttpContext.Current.Session` is `NULL` randomly. That's when you deploy your application on IIS, set the application pool `Maximum number of worker processes` to a value greater than 1 and use `In-Process Mode` for session state. – Khanh TO Jul 25 '14 at 09:44

3 Answers3

2

As confirmed in the comments, you're setting your application pool's Maximum number of worker processes to a value higher than 1 and use In-Process Mode for session state.

I'm pretty sure this is the problem of HttpContext.Current.Session being null randomly instead of HttpContext.Current.

InProc session state and multiple working processes are not compatible. With InProc session state, your session state is stored in worker processes memory and is not shared between worker processes => that results in session state being lost randomly when your requests are served by different processes.

In your case, setting Maximum number of worker processes to 1 should fix the issue.

enter image description here

If you're looking for a multiple working processes solution, you should store your session state out of process using either a session state service or a database: http://tutorials.csharp-online.net/ASP.NET_State_Management%E2%80%94Storing_Session_State_out_of_Process

Khanh TO
  • 48,509
  • 13
  • 99
  • 115
  • I'm sorry but I was mistaken, I just went to IIS, and I do have `Maximun Worker Processes` set to 1. Also note that it was indeed `HttpContext.Current` that was Null... Keep this answer though, its very good information that will most definitely help someone. – Dayan Jul 26 '14 at 14:40
1

This sounds suspiciously like it may be a session vs. forms authentication timing issue similar to Forms Authentication Timeout vs Session Timeout

Make sure your Forms timeout is at least 2x your session timeout.

Community
  • 1
  • 1
Keith Lawrence
  • 641
  • 4
  • 11
0

I think that you may need to verify that you have this set in your web.config

<httpRuntime targetFramework="4.5" />

It seems that you may not have the task friendly awaits and that will cause unexpected behaviors such as this. You may refer to the similar problem posed here and the solution.

Now, you may not be using Asynchronous code in your soution, but that doesn't mean that it isn't being used by the framework. Regardless, I think it is worth a shot.

Community
  • 1
  • 1
Charmless
  • 61
  • 2