24

I've built an MVC Core (Framework) application and I use Identity to login. When I click "Remember me" option all is ok on my develop machine, but after deploy on server machine, "remember me" doesn't maintain login after 30 minutes.

I tried to check if cookie expiry date is set and seems to be ok, also on server machine the cookie seems well set. You can see my cookies detail in following image:

enter image description here

Can anyone help me to solve this issue?

Thanks in advance for your reply :)

EDIT:

As required by Orhun, I add below my Startup.cs content:

public partial class Startup
{
    public SymmetricSecurityKey signingKey;

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

        if (env.IsDevelopment())
        {
            // For more details on using the user secret store see https://go.microsoft.com/fwlink/?LinkID=532709
            builder.AddUserSecrets<Startup>();
        }

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

    public IConfigurationRoot Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {

        ///////////////////////////
        // Custom Services - START
        ///////////////////////////

        string conn = CreateConnectionString(Configuration.GetConnectionString("TiesseWebConnection"));
        services.AddScoped<System.Data.Entity.DbContext>((_) => new TiesseWeb.DAL.TiesseWebEntities(conn));  //Configuration["Data:DefaultConnection:ConnectionString"]));


        // SESSION section
        services.AddMemoryCache();
        services.AddDistributedMemoryCache();
        services.AddSession();

        services.AddSingleton<IConfiguration>(Configuration);   // IConfiguration explicitly

        // Add functionality to inject IOptions<T> (important for inject Config object)
        services.AddOptions();


        // Add our Config object so it can be injected
        services.Configure<Settings>(Configuration.GetSection("Settings"));
        // Add our Config object so it can be injected
        services.AddScoped<Settings>();

        services.AddScoped<Tiesse.Web.BL.TiesseWebManager>();

        ///////////////////////////
        // Custom Services - END
        ///////////////////////////

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


        services.AddIdentity<ApplicationUser, ApplicationRole>(i =>
        {
            i.SecurityStampValidationInterval = TimeSpan.FromDays(14);
            //i.Cookies.ApplicationCookie.ExpireTimeSpan = TimeSpan.FromDays(14);
        })
        //services.AddIdentity<ApplicationUser, ApplicationRole>()//IdentityRole>()
          .AddEntityFrameworkStores<ApplicationDbContext, int>()
          .AddDefaultTokenProviders();

        services.AddMvc().AddJsonOptions(jsonOptions =>
        {
            jsonOptions.SerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
        }); ;

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

        // Adds Authorizations
        services.AddAuthorization(options =>
        {
            options.AddPolicy("Admin", policy => policy.RequireClaim("Admin"));
            options.AddPolicy("Admin-Utenti", policy => policy.RequireClaim("Admin-Utenti"));
            options.AddPolicy("Admin-Filiali", policy => policy.RequireClaim("Admin-Filiali"));
            options.AddPolicy("Admin-Reparti", policy => policy.RequireClaim("Admin-Reparti"));
            options.AddPolicy("GoogleDrive", policy => policy.RequireClaim("GoogleDrive"));
            options.AddPolicy("GoogleDrive-Gestione", policy => policy.RequireClaim("GoogleDrive-Gestione"));
            options.AddPolicy("GoogleDrive-Gestione-Struttura", policy => policy.RequireClaim("GoogleDrive-Gestione-Struttura"));
            options.AddPolicy("GoogleDrive-Consultazione", policy => policy.RequireClaim("GoogleDrive-Consultazione"));
            options.AddPolicy("Reports", policy => policy.RequireClaim("Reports"));
            options.AddPolicy("Reports-Test", policy => policy.RequireClaim("Reports-Test"));
        });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        // Custom settings
        app.UseSession();

        //// configures Bearer token Authentication
        //ConfigureAuth(app);
        ///////////////////


        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseDatabaseErrorPage();
            app.UseBrowserLink();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            //app.UseDeveloperExceptionPage();
            //app.UseDatabaseErrorPage();
            //app.UseBrowserLink();
        }

        app.UseStaticFiles();

        app.UseIdentity();

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            //CookieName = "MyWebCookie",
            //CookieDomain = "http://devweb01:81",      // uncomment when deploy
            CookieHttpOnly = true,
            CookieSecure = CookieSecurePolicy.Always,
            ExpireTimeSpan = TimeSpan.FromDays(30),
            SlidingExpiration = true,
            AutomaticAuthenticate = true,
            AutomaticChallenge = true
            //AuthenticationScheme = "MyeWebCookie"
        });

        app.UseGoogleAuthentication(new GoogleOptions()
        {
            // following Goggle Secrets data have been hardcoded because Configuration with Secrets.json works only in development environment
            ClientId = "XXXXXXX....",
            ClientSecret = "XXXXXXX....",
            AutomaticAuthenticate = true
            //SignInScheme = "MyWebCookie"
        });

        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }

    #region Methods

    public static string CreateConnectionString(string providerConnectionString)
    {
        var entityBuilder = new EntityConnectionStringBuilder();

        // use your ADO.NET connection string
        entityBuilder.ProviderConnectionString = providerConnectionString;

        entityBuilder.Provider = "System.Data.SqlClient";

        // Set the Metadata location.
        entityBuilder.Metadata = @"res://*/TiesseWebDB.csdl|res://*/TiesseWebDB.ssdl|res://*/TiesseWebDB.msl";

        return entityBuilder.ConnectionString;
    }

    #endregion
}
Badozvora
  • 383
  • 1
  • 3
  • 15
  • Can you share your Startup.cs file? – Orhun Sep 20 '17 at 13:38
  • Did I understand you right, the problem was solved with uncommenting "CookieDomain" ? I racked my brains trying to fix this issue... – dantey89 Oct 20 '17 at 08:43
  • Hi dantey89, at the moment I didn't solve the problem. I think is a problem related to the Web Server machine because I deployed my web application on 2 servers and in one of these it works but I didn't understand why. Did you find a solution? Any suggestions? thanks – Badozvora Nov 07 '17 at 14:28
  • I going to post an answer and describe what I did. – dantey89 Nov 10 '17 at 07:35

1 Answers1

29

I faced same issue. I couldn't solve it for a long time. But few days ago I found solution. As you mentioned in comments the problem is machine key. I don't know why, but application generates new machine key every time when it has been restarted. So way to solve problem is to force application to use constant key. To do it you need to add code like this in startup:

        public void ConfigureServices(IServiceCollection services)
        {

            var environment = services.BuildServiceProvider().GetRequiredService<IHostingEnvironment>();


            services.AddDataProtection()
                    .SetApplicationName($"my-app-{environment.EnvironmentName}")
                    .PersistKeysToFileSystem(new DirectoryInfo($@"{environment.ContentRootPath}\keys"));

           ...

        }

After application's start in "key" folder you will find xml which contains your machine key. More details you can find here

dantey89
  • 2,167
  • 24
  • 37
  • Hi dantey89, that's fantastic I solved my problem following your instructions and link you posted. Thank you veeeery much ;) – Badozvora Dec 20 '17 at 08:39
  • SO suggests not to answer with "thanks", but I will do that. Your answer really saved lots of hours for me. I dunno what's the root problem, but face it already in the second project - and only on the project site – Andrey Pesoshin Nov 05 '18 at 21:09
  • Hey, this answer solved my issue. I spent so many hours. I even payed someone to fix it. – emboole Feb 04 '19 at 20:15
  • what the issue with 3.1? The only problem you could face is that IHostingEnvironment was renamed to the IWebHostEnvironment – dantey89 May 12 '20 at 20:41
  • For other types of storage like EF Core: https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/implementation/key-storage-providers?view=aspnetcore-3.1&tabs=visual-studio#entity-framework-core – ctyar Oct 19 '20 at 13:44
  • In asp.net core 3 I get warning: warning ASP0000: Calling 'BuildServiceProvider' from application code results in an additional copy of singleton services being created. – user1238784 Nov 05 '20 at 07:06
  • 2
    It's absolutely infuriating that this is not mentioned in the documentation or included in the starter template, so much time wasted trying to figure out why auth cookies were expiring in 30 minutes. – Scott Wilson Feb 26 '21 at 17:49
  • @user1238784 You can solve that by injecting the environment inside constructor instead of calling GetRequiredService. Injection is now supported (it wasn't supported when dantey89 answered this in 2017). – Rosdi Kasim May 03 '21 at 06:58