Perpetually misplacing session state in ASP.NET

My website keeps losing session state, logging users out without explanation. I've been troubleshooting but can't pinpoint the cause.

I implemented an action filter to verify if the UserSession exists; if not, it checks if the user is authenticated and tries to recreate the session based on the user's ID.

If authentication fails, users are redirected to the login page. There's also code to check for ajax requests, set the status code to 403, and handle redirections in JavaScript.

This is my Action Filter:

public override void OnActionExecuting(ActionExecutingContext filterContext) {
    SecuredController baseController = filterContext.Controller as SecuredController;

    // Check if session is available
    if (filterContext.HttpContext.Session["UserSession"] == null) {

        if (!filterContext.HttpContext.User.Identity.IsAuthenticated) {
            if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest()) {
                filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
                filterContext.Result = new JsonResult {
                    Data = new { Error = "Unavailable", Url = "~/Account/Login" },
                    JsonRequestBehavior = JsonRequestBehavior.AllowGet
                };
                return;
            }
            if (!string.IsNullOrEmpty(HttpContext.Current.Request.RawUrl)) {
                string returnUrl = HttpUtility.UrlEncode(HttpContext.Current.Request.RawUrl);
                HttpContext.Current.Response.Redirect("~/Account/Login?returnUrl=" + returnUrl);
            } else {
                HttpContext.Current.Response.Redirect("~/Account/Login");
            }
        }

        string userId = filterContext.HttpContext.User.Identity.GetUserId();

        Web.Helpers.Common common = new Helpers.Common();
        UserSession userSession = common.GetUserSession(userId);

        filterContext.HttpContext.Session["UserSession"] = userSession;
    }

    baseController.CurrentUser = (UserSession)filterContext.HttpContext.Session["UserSession"];

    base.OnActionExecuting(filterContext);
}

And here's my Javascript Code:

$.ajax({
    type: method,
    url: rootUrl + serviceUrl,
    async: aSync,
    data: dataParameters,
    cache: false,
    beforeSend: function() {
        if (targetProgressContainer === undefined) {
            return;
        }
        if ($(targetProgressContainer).length === 0) {
            console.log('The Progress Container Div "' + targetProgressContainer + ' could not be found!');
            return;
        }

        $(targetProgressContainer).html($(_progressContainer).html());
    },
    statusCode: {
        403: function(data) {
            window.top.location.href = sessionEndedUrl;
        }
    },
    success: function(responseData, status, xhr) {
        successCallback(responseData);
    },
    error: function(request, textStatus, errorThrown) {
        errorCallback(request, textStatus, errorThrown);
    }
});

This is my Startup.ConfigureAuth method:

app.CreatePerOwinContext(ApplicationDbContext.Create);
    app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
    app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

    app.UseCookieAuthentication(new CookieAuthenticationOptions {
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        LoginPath = new PathString("/Account/Login"),
        Provider = new CookieAuthenticationProvider {
            OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                validateInterval: TimeSpan.FromMinutes(30),
                regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
        },

        SlidingExpiration = true,
        ExpireTimeSpan = TimeSpan.FromDays(30)
    });
    app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

    app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));

Additionally, rules have been added to ensure the user's URL always includes the full domain:

<rules>
    <rule name="Add www prefix to example.com domain" stopProcessing="true">
      <match url="(.*)" />
      <conditions>
        <add input="{HTTP_HOST}" pattern="^example\.com" />
      </conditions>
      <action type="Redirect" url="http://www.example.com/{R:1}" />
    </rule>
  </rules>

Any suggestions or insights would be greatly appreciated!

Answer №1

You previously mentioned that the issue is sporadic - it typically works well but occasionally the user gets unexpectedly logged out? I have to ask, are you using "in-proc" sessions and running your application on a web farm?

The symptoms you described align with a scenario where your app operates behind a load balancer and utilizes "in-proc" sessions. In such instances, if the load balancer switches the request to a different server, the user's session data might not be available leading to a redirect to the login page.

UPDATE 1

While examining the session aspect, it appears the issue may stem from the authentication cookie. If the encryption/decryption keys in the <machineKey> section of your web.config differ across servers within the web farm, the authentication cookie encrypted on one server may not decrypt properly on another, potentially setting the "IsAuthenticated" property to false. Have you configured these settings as outlined here: msdn.microsoft.com/en-us/library/eb0zx8fc.aspx?

UPDATE 2 - how to add machine key to your web.config

In response to comments received, here's a straightforward method to incorporate the machine key into your application (example based on IIS7).

  1. Navigate to your local IIS, select your site, and double-click the "Machine Key" icon in the features pane.

https://i.sstatic.net/fTKjL.png

If your application isn't hosted in local IIS, you can first generate keys on a test site and then transfer the configuration section to your app's web.config later.

  1. Click on "Generate Keys"

https://i.sstatic.net/KkOTL.png

  1. Verify the web.config of your site

https://i.sstatic.net/RXt6Z.png

  1. Your generated keys will resemble the example shown below

https://i.sstatic.net/te89L.png

You can easily copy the <machineKey> section into your application's web.config now.

Answer №2

Our team faced a similar issue within our program, and we believe sharing our experience could benefit you. The authentication token is typically saved as a cookie, but we noticed that our application was also storing a multitude of other data in cookies. Some of these cookies had dynamically generated names. It came to our attention that various web browsers impose restrictions on the number of cookies they can store for a particular website. As a result, older unmodified cookies are automatically discarded. If you fail to refresh your authentication cookie regularly, it ends up being the oldest one and ultimately gets deleted. To tackle this problem, we decided to transition most of our cookie usage to local storage instead. By doing so, we were able to maintain a well-defined and limited list of cookies.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for dynamically resizing a div element as a user scrolls, allowing it to expand and contract based on

I was working on a project and everything seemed easy until I hit a roadblock. What I am trying to achieve is expanding a div to 100% when scrolling down to the bottom of the div, then shrink it back to 90% at the bottom and do the reverse when scrolling ...

Javascript is unable to find the ID of an HTML form

Struggling with a basic HTML form and a JavaScript form validator that aims to verify input in the "first name" field. However, I'm having trouble retrieving the first name form value to validate it properly in JS. Below is the relevant HTML code sni ...

Easily modify and manage state on-the-fly using TextFields

Is the title conveying my intentions clearly? If not, please let me know. Essentially, I am looking to create a component that generates a form based on a JSON file. For example, if someone clicks on "light" in the navbar, I want the form to display fields ...

Is there a way for me to receive the response from the this.$store.dispatch method in vue.js 2?

Here is the structure of my component : <script> export default{ props:['search','category','shop'], ... methods: { getVueItems: function(page) { this.$store.disp ...

Ways to retrieve and upload a blob from a file object

After receiving a file object via an HTML input on-change event in my component.html: onFileSelected(event) { this.selectedFile = event.target.files[0]; } Now, I need to send a POST request to upload the image to the database. The database only acc ...

Interactive Web Component featuring embedded content

How to Use : <uc1:WindowControl ID="Window_ExpoNews" runat="server" Height="265px" Title="Expo News" Width="100%"> <ContentTemplate> This user control's content will be displayed in the center cell. ...

Techniques for dynamically counting rows in a table using JavaScript

I'm working on a system to create and delete rows, and I want each row to have a unique row number displayed under "Num." However, I'm having trouble implementing this feature. EDIT I found a jQuery snippet that counts the first row but not t ...

Enhancing WooCommerce by including additional text following the price for specific shipping methods

I need assistance with including a short message like "(incl. VAT)", following the shipping-price display on the checkout page. The challenge lies in ensuring that this message only appears for a specific shipping method within Zone 1 (zone_id=1), but I&a ...

Is there a way to verify if the meteor.call function was executed successfully?

In my meteor/react application, I am working with two components. I need to pass a method from one component to the other: saveNewUsername(newUsername) { Meteor.call('setNewUsername', newUsername, (error) => { if(error) { ...

Issue encountered when attempting to store the initial element in the todo array

I am facing an issue with my basic todo application in React using react-hooks. Whenever I try to take my inputValue and assign it to an object to save it in my items array, it doesn't seem to work. The first assignment after the onSubmit action res ...

Expanding and Shrinking Text Areas with Jquery

I am looking to create a textarea that comes preloaded with content. The height of the textarea should adjust to auto when there is content present, but switch to a height of 4.2rem when it is empty. Additionally, I want the textarea to dynamically increas ...

Unable to loop through a list in JavaScript

<script type="text/javascript"> window.onload = function () { for (var i=0;i<@Model.listsinfo.Count;i++) { $('#work').append($('<div class="col-md-3" id="temp"><label for="tex ...

Is there a way to initiate a jquery function upon loading the page, while ensuring its continued user interaction?

On my webpage, there is a JavaScript function using jQuery that I want to automatically start when the page loads. At the same time, I want to ensure that users can still interact with this function. This particular function involves selecting a link tha ...

Executing conditions in a JavaScript browser

Currently, I am utilizing a JS library known as fallback.js. This library loads all its <link> elements at the bottom of the <head> tag. However, I am facing an issue where I need to load certain stylesheets at the very end, but they require IE ...

What is the best way to access a ViewChild element in the parent component without rendering it?

I am currently utilizing a component within another component in the following manner: <inline-help> <help-title>{{::'lang.whatIsThis'|i18n}}</help-title> <help-body i18n="lang.helpBody">& ...

After removing an item from the array, React fails to display the updated render

As a newcomer, I am struggling with a particular issue. I have implemented a delete button for each item in a list. When the button is clicked, the object in the firstItems array is successfully deleted (as confirmed by logging the array to the console), b ...

Is there a way to showcase my information on flash cards using JavaScript?

Currently, I am developing a full stack application that utilizes JavaScript on both the front and back end. This application allows users to create their own flashcards set. Upon clicking "View Cards," the data is fetched and the question-answer pair is d ...

JavaScript - Issue arises when evaluating the sine of complex numbers of large magnitudes

I have developed a unique sine calculator that can handle the evaluation of the sine of complex numbers by utilizing polar coordinates and computing part of the infinite series defining the sine function. The calculator performs smoothly when dealing wit ...

The perfect timing for incorporating a JavaScript MVC framework into your project

As part of my job at a small web agency specializing in web applications for startups, I'm urging my boss to invest more in developing strong client-side frameworks using Javascript MVC standards like BackboneJS and templating with Underscore. I unde ...

Struggling to create a complete Absolute URL

www.baxter.com source page reveals that many of the href links start with the word baxter, for example: href="/baxter/corporate.page?">About Baxter< Attempting to construct an absolute URL by combining the base url, www.baxter.com, with the relativ ...