4

I'm using redis cache for saving some stuff in my project.

I am using Azure (WebApp), and when I do a SWAP between my preproduction environment to production, the user session is lost and he need to relogin in my web page.

I'm using Identity 3.0, with UseCookieAuthentication. I would like to store the "session" in Redis for solving my problem when I do the swap.

I don't found information about it, any ideas? Thanks

Startup.cs Code ConfigureServices:

public void ConfigureServices(IServiceCollection services)
        {

                        // Add framework services.
            services.AddApplicationInsightsTelemetry(Configuration);

            // Registers MongoDB conventions for ignoring default and blank fields
            // NOTE: if you have registered default conventions elsewhere, probably don't need to do this
            //RegisterClassMap<ApplicationUser, IdentityRole, ObjectId>.Init();

            AutoMapperWebConfiguration.Configure();

            services.AddSingleton<ApplicationDbContext>();

            // Add Mongo Identity services to the services container.
            services.AddIdentity<ApplicationUser, IdentityRole>(o =>
            {
                // configure identity options
                o.Password.RequireDigit = false;
                o.Password.RequireLowercase = false;
                o.Password.RequireUppercase = false;
                o.Password.RequireNonLetterOrDigit = false;
                o.Password.RequiredLength = 6;
                o.User.RequireUniqueEmail = true;
                o.Cookies.ApplicationCookie.CookieSecure = CookieSecureOption.SameAsRequest;
                o.Cookies.ApplicationCookie.CookieName = "MyCookie";
            })
                .AddMongoStores<ApplicationDbContext, ApplicationUser, IdentityRole>()
                .AddDefaultTokenProviders();

            services.AddSession(options =>
            {
                options.IdleTimeout = TimeSpan.FromMinutes(60);
                options.CookieName = "MyCookie";
            });

            services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));

            services.AddLocalization(options => options.ResourcesPath = "Resources");

            // Caching This will add the Redis implementation of IDistributedCache
            services.AddRedisCache();

            services.Configure<RedisCacheOptions>(options =>
            {
                options.Configuration = Configuration["RedisConnection"];
            });




            services.AddCaching();

            // Add MVC services to the services container.
            services.AddMvc(options =>
            {
                options.CacheProfiles.Add("OneDay",
                    new CacheProfile()
                    {
                        Duration = 86400,
                        Location = ResponseCacheLocation.Any
                    });

                options.CacheProfiles.Add("OneMinute",
                    new CacheProfile()
                    {
                        Duration = 60,
                        Location = ResponseCacheLocation.Any
                    });

            })
                .AddViewLocalization(options => options.ResourcesPath = "Resources")
                .AddDataAnnotationsLocalization();



            services.Configure<AppOptions>(Configuration.GetSection("AppOptions"));



        }

Startup.cs Code

// 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)
        {
            //
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            app.UseApplicationInsightsRequestTelemetry();

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

            }

            app.UseSession();

            app.UseIISPlatformHandler(options => options.AuthenticationDescriptions.Clear());

            app.UseApplicationInsightsExceptionTelemetry();

            app.UseStaticFiles();

            app.UseIdentity();


            app.UseCookieAuthentication(options =>
            {
                options.AutomaticAuthenticate = true;
                options.LoginPath = new PathString("/Account/Login");
                options.AutomaticChallenge = true;
            });

            var requestLocalizationOptions = new RequestLocalizationOptions
            {
                // Set options here to change middleware behavior
                SupportedCultures = new List<CultureInfo>
                {
                    new CultureInfo("en-US"),
                    new CultureInfo("es-ES")
                },
                SupportedUICultures = new List<CultureInfo>
                {
                    new CultureInfo("en-US"),
                    new CultureInfo("es-ES")

                },
                RequestCultureProviders = new List<IRequestCultureProvider>
                {
                    new CookieRequestCultureProvider
                    {
                        CookieName = "_cultureLocalization"
                    },
                    new QueryStringRequestCultureProvider(),
                    new AcceptLanguageHeaderRequestCultureProvider
                    {

                    }

                }
            };

            app.UseRequestLocalization(requestLocalizationOptions, defaultRequestCulture: new RequestCulture("en-US"));

            app.UseFacebookAuthentication(options =>
            {
                options.AppId = "*****";
                options.AppSecret = "****";
            });

            app.UseGoogleAuthentication(options =>
            {
                options.ClientId = "*****";
                options.ClientSecret = "***";
            });



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

                routes.MapRoute(
                    name: "view",
                    template: "{customName}/{id}",
                    defaults: new { controller = "View", action = "Index" });

            });

        }
juvchan
  • 6,113
  • 2
  • 22
  • 35
chemitaxis
  • 13,889
  • 17
  • 74
  • 125

1 Answers1

2

Session is not linked to Authentication, you're attempting to solve it in the wrong way.

All forms authentication tickets and cookies are encrypted and signed using the data protection layer. The problem you are encountering is due to the encryption keys not being saved, and applications being isolated from each other.

In order to solve it you must share both the encryption keys and set an application name in your code. In all honesty I'd recommend you don't. Pre-production is not a live service, and you shouldn't be able to authenticate to both at once.

If you feel like you must do this then you need to share the encryption key-ring, and set a fixed application name. You can share keys via a shared folder, or by storing them in a shared location, such as SQL, or Azure storage. In order to do so you'd have to write your own keyring provider, by implementing an IXmlRepository. Once you have your keys shared then you can set a fixed application identifier by using SetApplicationName during data protection configuration.

blowdart
  • 55,577
  • 12
  • 114
  • 149
  • Thank you for your answer, but I don't want to share the session between environment. My problem is that when I do the swap, the user lost the session in production. – chemitaxis Apr 07 '16 at 15:38
  • Your question mentions logins and having to login again. So is the relogin the problem? Or session? Or both? – blowdart Apr 07 '16 at 15:50
  • The user needs to relogin in production after swap – chemitaxis Apr 07 '16 at 15:58
  • So my answer is correct. Logins are not stored in session. You must share the encryption keys so login on one server can be decrypted on another – blowdart Apr 07 '16 at 15:59
  • Thanks, do i have any other alternative? Storing the logins in other place? This is too bad option for me... – chemitaxis Apr 07 '16 at 16:23
  • No. Logins have always been cookies, because it prevents session highjacking vulnerabilities. – blowdart Apr 07 '16 at 16:25
  • Thanks again, Can i store the encription keys in my code? – chemitaxis Apr 07 '16 at 16:29
  • The keys rotate on a regular basis. You could I guess and set a really large key rotation period but it's a really really bad idea. – blowdart Apr 07 '16 at 16:33