0

Final Update: It turned out that the issue was caused by authentication timeouts rather than session timeouts. These are different things and need to be set separately. Here is the fix I ended up with:

Create a timeout value in appsettings.json. This value will be used to configure session and authorization timeouts. (You can name this whatever you want.)

"IdleTimeoutMinutes": 60,

Set session timeout in Startup.cs.

services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromMinutes(Configuration.GetValue<double>("IdleTimeoutMinutes"));
    options.Cookie.HttpOnly = true;
    options.Cookie.IsEssential = true;
});       

Configure authentication timeout in Startup.cs.

services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
    .AddNegotiate()
    .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme,
options =>
{
    options.LoginPath = new PathString("/Account/Login/LogOn/");
    options.AccessDeniedPath = new PathString("/Account/Login/AccessDenied/");
    options.SlidingExpiration = true;
    options.ExpireTimeSpan = TimeSpan.FromMinutes(Configuration.GetValue<double>("IdleTimeoutMinutes"));
});

Configure authentication cookie timeout in LoginController.cs.

await HttpContext.SignInAsync(
    CookieAuthenticationDefaults.AuthenticationScheme, claims.Get(currentUser),
        new AuthenticationProperties
        {
            ExpiresUtc = DateTime.UtcNow.AddMinutes(Configuration.GetValue<double>("IdleTimeoutMinutes")),
            IsPersistent = true,
            AllowRefresh = true
        });

I found this information here: https://www.blakepell.com/blog/asp-net-core-cookie-authentication-timing-out

(Archived page: https://web.archive.org/web/20220411151611/https://www.blakepell.com/blog/asp-net-core-cookie-authentication-timing-out)

Note that the Cookie.Expiration setting described at this link no longer works. It returns a error "Cookie.Expiration is ignored, use ExpireTimeSpan instead". I left out that line and everything else worked.

Update: As a test, I changed the session IdleTimeout value from 30 minutes to 10 seconds and discovered that the session timeout is, in fact, working as expected. There is a different, unknown, timeout that is breaking things after 15 minutes, regardless of the session IdleTimeout value. Where is this 15 minute timeout coming from?

Things that break after IdleTimeout seconds:

  1. HttpContext.Session.GetString(SessionKeySheetData) in .cs code returns a "Value cannot be null" error. This is expected. The session IdleTimeout is working correctly.

Things that break after 15 minutes idle, regardless of the session IdleTimeout value:

  1. JavaScript Ajax calls result in an authentication popup asking for user name and password. This does not occur anywhere else in this application that uses Windows authentication and it does not accept my usual Windows login user name and password. I can work around this by adding a [AllowAnonymous] directive to the relevant code.
  2. Context.Request.Form("<form field id>") in .cshtml code returns a "Incorrect Content-type" error.

Apparently there are two idle timeouts here. The session timeout, controlled by the IdleTimeout value, is working as expected. But there is another timeout of 15 minutes that is not related to the session timeout. Where is this other timeout coming from and how can it be changed?

Original question:

I'm a beginning C# coder and I have been tasked with maintaining an existing web application written in C# with .NET 5.0. The current session timeout seems to be 15 minutes and I would like to increase it. This question has been asked and answered many times, but before flagging this as a duplicate, understand that most of those questions and answers are for older versions of .NET and are not applicable to the current version. Also, nearly everyone has a different answer, and I have not been able to get any of them to work.

The answers I have found fall into four categories:

  1. IIS configuration changes. For development I am using the IIS Express that comes with VS 2019. I have installed the latest IIS Manager app, but nothing I see there matches the answers I find online. I presume those answers are for older versions of IIS, and I do not see any session timeout configuration in the current version of IIS Manager. Example: https://beansoftware.com/ASP.NET-Tutorials/Session-Timeout-Expiration.aspx

  2. Project configuration changes. I have found many, many pages suggesting to set sessionState timeout="<minutes>" in web.config. However my project does not have a web.config and it is my understanding that this is used only in older .NET versions. Example: Session timeout in ASP.NET

  3. Create a JavaScript/Ajax function to keep the session alive by calling a dummy HTTP Handler. I haven't been able to get this to work and it seems like an ugly hack if there is a way to simply change the timeout configuration. Example: Keeping ASP.NET Session Open / Alive

  4. Change the session timeout programmatically. I think this would be a good option if I could get it to work, but adding public TimeSpan IdleTimeout = TimeSpan.Parse("01:00:00"); to the code does not seem to have any effect. Example: https://learn.microsoft.com/en-us/dotnet/api/system.web.configuration.processmodelsection.idletimeout?view=netframework-4.8

Any help would be appreciated.

Edit: Added Startup.cs below, as requested.

using ILawProductionApp.Domain.Context;
using ILawProductionApp.Domain.Helpers.Sheets;
using ILawProductionApp.Domain.Shared.Contracts;
using ILawProductionApp.Domain.Shared.Repository;
using ILawProductionApp.Infrastructure.Contexts;
using ILawProductionApp.Infrastructure.Interfaces;
using ILawProductionApp.Infrastructure.Interfaces.Repositories;
using ILawProductionApp.UI.Areas.Account.Managers.Authorization;
using ILawProductionApp.UI.Helpers;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.Negotiate;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;

namespace ILawProductionApp.UI
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {            
            services.AddHttpContextAccessor();
            services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
                .AddNegotiate()
                .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme,
            options =>
            {
                options.LoginPath = new PathString("/Account/Login/LogOn/");
                options.AccessDeniedPath = new PathString("/Account/Login/AccessDenied/");

            });

            services.AddDbContext<AppIdentityDbContext>(options => options.
           UseOracle(Environment.GetEnvironmentVariable(Configuration["EnvironmentVars:DefaultConnectionString"])));

            services.AddMvc(config =>
            {
                var policy = new AuthorizationPolicyBuilder()
                                 .RequireAuthenticatedUser()
                                 .Build();
                config.Filters.Add(new AuthorizeFilter(policy));
            });

            services.AddSingleton<IAuthorizationPolicyProvider, LawPermissionPolicyProvider>();
            services.AddScoped<IAuthorizationHandler, RolesAuthorizationHandler>();
            services.AddScoped<IAuthorizationHandler, LawPermissionAuthorizationHandler>();
            //services.AddScoped<IAuthorizationHandler, ProcessCalApp2AuthorizationHandler>();
            //services.AddScoped<IAuthorizationHandler, ProccessGlassAppAuthorizationHandler>();
            services.AddDistributedMemoryCache();
            services.AddSession(options =>
            {
                options.IdleTimeout = TimeSpan.FromSeconds(Configuration.GetValue<double>("SessionTimeoutSeconds"));
                options.Cookie.HttpOnly = true;
                options.Cookie.IsEssential = true;
            });       
            services.AddControllersWithViews().AddSessionStateTempDataProvider();
            services.AddDbContext<ApplicationILawDbContext>(options => options.UseLazyLoadingProxies()
            .UseOracle(Environment.GetEnvironmentVariable(Configuration["EnvironmentVars:DefaultConnectionString"])));
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            services.AddScoped<IAdminUnitOfWork, AdminUnitOfWork>();
            services.AddScoped<IUnitOfWork, UnitOfWork>();
            services.AddScoped<ICa2RunUtil, Ca2RunUtil>();
            services.AddScoped<ILawGaRunUtil, LawGaRunUtil>();
            services.AddTransient<ISheetModifier, SheetModifier>();
 
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }
            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseRouting();
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseSession();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                   name: "Adminstration",
                   pattern: "{area:exists}/{controller=Admin}/{action=Index}/{id?}");
                               
                endpoints.MapControllerRoute(
                      name: "Account",
                      pattern: "{area:exists}/{controller=Account}/{action=Index}/{id?}"
                );

                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
                endpoints.MapRazorPages();
            });
        }
    }
}
  • Does this help? [ASP.NET Core Session Timeout](https://stackoverflow.com/questions/61145385/asp-net-core-session-timeout) - also [docs ref](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.builder.sessionoptions.idletimeout?view=aspnetcore-6.0#microsoft-aspnetcore-builder-sessionoptions-idletimeout) - _"The `IdleTimeout` indicates how long the session can be idle before its contents are abandoned. Each session access resets the timeout. Note this only applies to the content of the session, not the cookie."_ – stuartd Apr 05 '22 at 18:32
  • 1
    @stuartd There are three answers at that link, all with 0 points, and none of them are accepted. Which one did you think would be helpful? – Sagebrush Gardener Apr 05 '22 at 18:44
  • @SagebrushGardener Are you using `ASP.NET CORE`? – Rahul Sharma Apr 05 '22 at 18:51
  • @RahulSharma When I look at project properties, it shows that the Target Framework is ".NET 5.0". If I understand correctly, this is a .NET CORE release. – Sagebrush Gardener Apr 05 '22 at 19:00
  • @SagebrushGardener What do you mean when you say my project does not have a `web.config`? Is there a `appsettings.json` file in your project? – Rahul Sharma Apr 05 '22 at 19:01
  • @RahulSharma Yes, I do have an appsettings.json and also a startup.cs. – Sagebrush Gardener Apr 05 '22 at 19:03
  • did you get a chance to check [this](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/app-state?view=aspnetcore-5.0) Microsoft doc? – codeninja.sj Apr 05 '22 at 19:05
  • @SagebrushGardener Please post your `Startup.cs` so we can see how you are setting up your session? – Rahul Sharma Apr 05 '22 at 19:05
  • @RahulSharma Added Startup.cs as requested – Sagebrush Gardener Apr 05 '22 at 19:12
  • @RahulSharma Startup.cs does have a options.IdleTimeout setting, but the value (from appsettings.json) is 1800 (30 minutes), which does not match the session timeout of 15 minutes that I am seeing. – Sagebrush Gardener Apr 05 '22 at 19:14
  • @SagebrushGardener You can refer to this answer for your case: https://stackoverflow.com/questions/62770208/asp-net-core-3-0-how-to-increase-idle-timeout – Rahul Sharma Apr 05 '22 at 19:19
  • @RahulSharma My VS does not like the AddDataProtection line. "services.AddDataProtection does not exist in the current context". I tried "using Microsoft.AspNetCore.DataProtection;" but that does not fix the problem and VS tells me it is unnecessary. – Sagebrush Gardener Apr 05 '22 at 21:32
  • @SagebrushGardener Okay, can you tell me where this observation is being done? Is it on your local development environment or where the application is deployed on web server? – Rahul Sharma Apr 06 '22 at 08:43
  • @RahulSharma I am observing these issues on my local development server, but the same issues occur in the deployed application. I have discovered new information. Kindly see the update at the top of the page. – Sagebrush Gardener Apr 06 '22 at 15:49
  • @SagebrushGardener For point 1 regarding AJAX calls , I think it could be this issue that I also faced at some point: https://stackoverflow.com/questions/57317232/session-state-being-cleared-or-lost-after-one-ajax-call-to-net-core-2-1-applica . I hope this solves your issue entirely. – Rahul Sharma Apr 06 '22 at 16:30
  • @RahulSharma I found the answer here: https://www.blakepell.com/blog/asp-net-core-cookie-authentication-timing-out It turned out that the problem was authentication cookie timeout rather than session timeout. I hadn't realized these were separate things. I'll post an update shortly. – Sagebrush Gardener Apr 12 '22 at 18:45

0 Answers0