Search code examples
c#sessionasp.net-coreasp.net-core-identity

Asp.Net Core 2 MVC Authentication: Add different session times


I'm new to coding in general and specifically Asp.Net core so excuse my noobiness here. I'm really hoping for some help with my project. I'm at the moment tearing my hair with adding different TimeSpan to different users while logging in. Say for example user from company 1 login and will, hopefully, get a session time of say 60 min (of idle time that is) but when user from company 2 login the session time would be maybe the default of 20 min.

UPDATE: My Target framework is .Net Framework 4.6.1 but the code is prepared for .Net Core 2.1 and I'm running the Identity package v2.1.2

My startup class looks like this:

public class Startup
{
    public Startup(IHostingEnvironment env)
    {
        CultureInfo.CurrentCulture = new CultureInfo("sv-SE");
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

        if (env.IsDevelopment())
        {
            builder.AddUserSecrets<Startup>();
        }

        builder.AddInMemoryCollection(GetBeanstalkEnvironmentProperties());
        builder.AddEnvironmentVariables();
        Configuration = builder.Build();
    }

    public IConfigurationRoot Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<IConfiguration>(Configuration);

        // Add framework services.
        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("ExternalServerConnection")));

        services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultTokenProviders();

        services.Configure<EmailConfiguration>(Configuration.GetSection("EmailConfiguration"));
        services.AddMvc();

        // Add application services.
        services.AddTransient<IEmailSender, AuthMessageSender>();
        services.AddTransient<ISmsSender, AuthMessageSender>();
    }

Login in controller is just as easy as this:

  public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
    {
        ViewData["ReturnUrl"] = returnUrl;
        if (ModelState.IsValid)
        {
            var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
            if (result.Succeeded)
            {
                _logger.LogInformation(1, "User logged in.");
                
                return RedirectToAction("...");
            ...

And after checking different solutions and forums a found this option to add the Timespan in Startup:

        services.Configure<SecurityStampValidatorOptions>(options =>
        {
            // Add validationinterval E.g how often the stamp is checked.
            options.ValidationInterval = TimeSpan.FromSeconds(10);
        });
        services.AddAuthentication().Services.ConfigureApplicationCookie(options =>
        {
            // Timespan extension if page is refreshed or navigated. Default is true.
            options.SlidingExpiration = true;
            // Cookie is valid at minimum 20 min.
            options.ExpireTimeSpan = TimeSpan.FromMinutes(20);
        });

This works fine but this is a global way of setting the session time for the users and will then apply to all users. So I hope someone have a good solution where I can use the above and still be able to add different values here options.ExpireTimeSpan = TimeSpan.FromMinutes(20); depending on the users preferences e.g. which company the user is a member of.

Other references and whats I've tried so far.

I've tried to figure this one out but did not succeed in my case. I guess I would need be first be able to check the user for claims (to see which company the user belongs to) and perhaps set a value for the specific company e.g. company 1 value 60 in a DB table then read that value and update the Timespan with that value from the Db or in a more "ugly" way just hardcode the value in Startup. Say that user belongs to a certain company which then should have a specific session value or if not set the default value of 20 min. For example in pseudo: (If user.companyID equals 1 then set options.ExpireTimeSpan = TimeSpan.FromMinutes(60)) and so on. My problem here is that of course I can´t access this values in startup, and probably shouldn't?

I've also tried this but that extends the cookie for the client side if I didn't get i wrong and doesn't effect the idle user time?! and it also uses Session which I guess I could add but I'm not sure that will be any good in my situation anyway.

I've tried to follow this documentation as much as possible as well. But the framework doesn't seem to support multiple/different ExpireTimeSpan settings.

Here is another question, that has no answers yet, with what I can tell the same problem as I'm having.


Solution

  • I solved it like this instead:

        services.Configure<SecurityStampValidatorOptions>(Configuration.GetSection("SecurityStampValidatorOptions"));
    
        services.AddAuthentication().Services.ConfigureApplicationCookie(options =>
                {
                    var cookieAuthenticationOptions = Configuration
                       .GetSection(nameof(CookieAuthenticationOptions))
                       .Get<CookieAuthenticationOptions>();
                    if (cookieAuthenticationOptions == null)
                        return;
    
                    options.ExpireTimeSpan = cookieAuthenticationOptions.ExpireTimeSpan;
                    options.SlidingExpiration = cookieAuthenticationOptions.SlidingExpiration;
                });
    

    Perhaps not the best way but then I can still use Services.ConfigureApplicationCookie options plus be able to override SecurityStampValidatorOptions and part of CookieAuthenticationOptions from settings (appsettings.json, ENV etc) and by that I can deploy the application with different settings.

    I couldn't find out how to configure the application cookie directly from the configuration section so a selected few attributes are set if the setting are available.

    So it didn't solve my real question but this is good enough because I've different deploy options and locations so it is a working walk-around.

    I can then for exempel do like this in appsettings.json:

    "SecurityStampValidatorOptions": {
      "ValidationInterval": "0.00:00:20"
        },
    "CookieAuthenticationOptions": {
       "SlidingExpiration": true,
       "ExpireTimeSpan": "0.02:00:00"
      }
    

    and override the default values and also on the upfront is that there are no hard-coded values in Startup.