简体   繁体   中英

.NET Core 3.1 Authentication Cookie Lost After Very Short Time

This project is still in development and works perfectly on my dev laptop, however, when I publish to my shared hosting web server (Winserve), this is when the functionality breaks.

Side note: I'm sure this was working fine a couple of weeks ago, but I could be wrong as I've spent most of my time running this on my dev laptop.

I have a basic asp.net Core 3.1 web application. I've added Cookie and Authentication information to the Configure() and ConfigureServices() sections in my startup.cs file.

I can log in to the application in PROD and my auth cookie seems to be set correctly, it's grabbing the roles from the db correctly and adding the relevant claims etc. and giving the user the correct Roles. I know this because the UI changes based on whether or not User.Identity.IsAuthenticated = true and whether or not User.IsInRole("Some Role") etc.

However, after a (not consistant) period of time, usually around 30-40 seconds, when I navigate to a page (Controller/Action) that needs the user to be authenticated (qualified with [Authorize]), I get redirected back to the Login page! This isn't just a redirect though, the user has been either signed out or the cookie no longer works. I've tried once every 5 seconds for about 30 seconds after this happens to get back to the 'Authorized' URL and every time I get pushed back to the Login page.

Within that time (before the 30-40 seconds), I can navigate to secured pages to my heart's content. All different pages and either one straight after the other, or leave a few second gap between navigations and it still works, right up until it doesn't!

Also, I've checked the Cookies in the browser inspector and the Cookie is definitely getting created, with a default expiry date of around 14 days I think it is. But it's way in the future regardless.

Here is my Startup.cs

public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews().AddRazorRuntimeCompilation().AddNewtonsoftJson();

            services.AddControllers().AddNewtonsoftJson();

            services.AddDistributedMemoryCache();

            services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
                    .AddCookie(options =>
                                {
                                    options.LoginPath = "/Login";
                                });

            services.AddAuthorization();

            services.AddSession(options =>
            {
                options.IdleTimeout = TimeSpan.FromMinutes(30);
                options.Cookie.HttpOnly = true;
                options.Cookie.IsEssential = true;
            });

            services.AddMvc().AddNewtonsoftJson();

            services.AddRazorPages().AddNewtonsoftJson();

        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseRouting();

            app.UseSession();

            app.UseAuthentication();

            app.UseAuthorization();

            app.UseHttpsRedirection();

            app.UseStaticFiles();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "areas",
                    pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");

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

        }

Here is my Login and logout methods in my HomeController...

public IActionResult Login(LoginViewModel model)
        {
            if (string.IsNullOrEmpty(model.Username) || string.IsNullOrEmpty(model.Password)) { model.Errors.Add("Must provide a Username & Password"); return View(model); }
            var _user = _authenticationBusinessService.AuthenticateUser(model.Username, model.Password);
            if (_user != null && _user.Id > 0) { return SignUserIn(model, _user); }
            else { model.Errors.Add("We're unable to authenticate you with the credentials provided"); }
            return View(model);
        }

        public IActionResult SignOut()
        {
            return SignUserOut();
        }

And the methods they call...

   private IActionResult SignUserIn(LoginViewModel model, UserDTO user)
        {
            var _claims = new List<Claim>
            {
                //User identity information
                new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
                new Claim(ClaimTypes.Name, user.Id.ToString()),
                new Claim("FirstName", user.Person.FirstName),
                new Claim("Surname", user.Person.Surname)
            };
            //Roles/Permissions
            _claims.AddRange(user.UserPermissionMaps.Select(x => new Claim(ClaimTypes.Role, x.Permission.Description)));

            var _claimsIdenity = new ClaimsIdentity(_claims, CookieAuthenticationDefaults.AuthenticationScheme);
            var _authProperties = new AuthenticationProperties() { IsPersistent = true, AllowRefresh = true };
            var _claimsPrincipal = new ClaimsPrincipal(_claimsIdenity);
            HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, _claimsPrincipal, _authProperties);

            if (string.IsNullOrEmpty(model.ReturnURL))
            {
                model.ReturnURL = "/";
            }
            return Redirect(model.ReturnURL);
        }
        private IActionResult SignUserOut()
        {
            HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
            HttpContext.Session.Clear();
            return RedirectToAction("Index");
        }

Example Cotroller that you need to be authorised to be able to access...

 [Area("Admin")]
    public class HomeController : Controller
    {
        [Authorize]
        public IActionResult Index()
        {
            return View();
        }
    }

I found the solution to my problem thanks to @weichch

There was an AppPool size limit set by my cheap hosting provider which was 200MB

My app, when loaded through IIS Express through VS, got to around 160-180MB and then fairly quickly, (just by doing a few things in my admin section for example), went above 200MB, but then sat around 220-230MB and didn't really move (so it's not a memeory leak problem).

I upgraded my plan with my provider to one where the AppPool can go to 512MB, and this completely solved the problem. It's been working fine ever since.

Thanks everyone 👍

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM