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.