Home > OS >  ASP.NET Core 6: Setting session timeout does not work
ASP.NET Core 6: Setting session timeout does not work

Time:01-13

I am trying to adjust my .NET Core 6 project to use a session timeout that is not the default.

Following the instructions on Microsoft.com, I have tried adding the following to my Program.cs:

//This should cause me to time out after 1 minute. It does not
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession(
    options => {
        options.Cookie.Name = ".MyWebsite.Session";
        options.IdleTimeout = TimeSpan.FromMinutes(1);
        options.Cookie.IsEssential = true;
    });

app.UseSession();

Is there something I am missing? I would like to increase the time it takes for a session to expire. As per the code above, when I log in to the program, my session should expire after 1 minute. It does. Same if I set the time to 4 hours. It seems to force me to log out around the 20 minute mark regardless of the settings I use.

The project was migrated from .NET Core 3.1, if that matters.

Here is the full code from the Program.cs file:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("MyConnection");
builder.Services.AddDbContext<DBContext>(options => options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddControllersWithViews();

builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession(
    options => {
        options.Cookie.Name = ".MyWebsite.Session";
        options.IdleTimeout = TimeSpan.FromMinutes(1);
        options.Cookie.IsEssential = true;
    });

//Add JWT bearer and denied paths
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        options.LoginPath = "/Account/Unauthorized/";
        options.AccessDeniedPath = "/Account/Forbidden/";
    })
            .AddJwtBearer(x =>
            {
                x.RequireHttpsMetadata = false;
                x.SaveToken = true;
                x.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidateAudience = false,
                    ValidateIssuerSigningKey = true,
                    ValidIssuer = builder.Configuration["Jwt:Issuer"],
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("Jwt:Key"))
                };
            });

//GDPR compliance
builder.Services.Configure<CookiePolicyOptions>(options =>
{
    options.CheckConsentNeeded = context => true;
    options.MinimumSameSitePolicy = SameSiteMode.None;
});

builder.Services.ConfigureNonBreakingSameSiteCookies();

builder.Services.ConfigureApplicationCookie(options =>
{
    options.Events = new CookieAuthenticationEvents
    {
        OnRedirectToLogin = x =>
        {
            x.Response.Redirect("https://localhost:44329/Expired/Index/000");
            return Task.CompletedTask;
        }
    };
    options.ExpireTimeSpan = TimeSpan.FromDays(14);
    options.SlidingExpiration = true;
});

//define policy for different authorization
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AdminOnly", policy => policy.RequireRole("Administrator"));
    options.AddPolicy("UsersOnly", policy => policy.RequireRole("User", "Editor", "Administrator"));
    options.AddPolicy("RequireApprovedUser", policy => policy.Requirements.Add(new ApprovedUserRequirement(true)));
});

builder.Services.AddScoped<IAuthorizationHandler, ApprovedUserRequirementHandler>();

//Data Protection configuration
var keysFolder = Path.Combine(builder.Environment.ContentRootPath, "Keys");
builder.Services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(keysFolder))
    .SetDefaultKeyLifetime(TimeSpan.FromDays(14));

builder.Services.AddIdentity<IdentityUser, IdentityRole>(options =>
{
    options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromHours(6);
    options.Lockout.MaxFailedAccessAttempts = 5;
    options.SignIn.RequireConfirmedAccount = true;
})
    .AddDefaultTokenProviders()
    .AddDefaultUI()
    .AddEntityFrameworkStores<DBContext>();

builder.Services.AddRazorPages();

builder.Services.AddScoped<IUserClaimsPrincipalFactory<IdentityUser>, UserClaimsPrincipalFactory<IdentityUser, IdentityRole>>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}
app.Use(async (context, next) =>
{
    await next();
    if (context.Response.StatusCode >= 400)
    {
        context.Request.Path = "/Error/Index/"   context.Response.StatusCode;
        await next();
    }
});

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.UseSession();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");
app.MapRazorPages();

app.Run();

CodePudding user response:

When using cookie authentication, the ExpireTimeSpan property is only used if when calling HttpContext.Authentication.SignInAsync we pass in an instance of AuthenticationProperties with IsPersistent set to true.

So, you may want the cookie to persist across browser sessions. This persistence should only be enabled with explicit user consent with a "Remember Me" checkbox on sign in or a similar mechanism. The code like this:

// using Microsoft.AspNetCore.Authentication;

await HttpContext.SignInAsync(
    CookieAuthenticationDefaults.AuthenticationScheme,
    new ClaimsPrincipal(claimsIdentity),
    new AuthenticationProperties
    {
        IsPersistent = true
    });

Besides, you can also set Absolute cookie expiration. The following code snippet creates an identity and corresponding cookie that lasts for 20 minutes. This ignores any sliding expiration settings previously configured.

// using Microsoft.AspNetCore.Authentication;

await HttpContext.SignInAsync(
    CookieAuthenticationDefaults.AuthenticationScheme,
    new ClaimsPrincipal(claimsIdentity),
    new AuthenticationProperties
    {
        IsPersistent = true,
        ExpiresUtc = DateTime.UtcNow.AddMinutes(20)
    });

More detail information, see Persistent cookies.

CodePudding user response:

Welp, I finally figured it out. Turns out using sessions for my authentication cookies just don't work. Probably something I did elsewhere.

I used the following to force my cookies to have a lifetime of 1 minute:

builder.Services.ConfigureApplicationCookie(options =>
{
    options.Cookie.Name = ".AspNetCore.Identity.Application";
    options.ExpireTimeSpan = TimeSpan.FromMinutes(1);
    options.Cookie.MaxAge = TimeSpan.FromMinutes(1);
    options.SlidingExpiration = true;
});

Key here is to use options.Cookie.MaxAge in addition to options.ExpireTimeSpan.

Giving users more than the 20-odd minute default time is much easier now.

  • Related