ASP.NET Core Identity & Cookies
Asked Answered
T

3

4

I have an ASP.NET Core site using AspNetCore.Identity.EntityFrameworkCore 1.1.1 and cookies to authorize/authenticate my users. No matter what I choose as my setting in the code below, the cookie expires after about 20 minutes and I can't figure why. The website will then no longer work unless you close the browser and clear the history/cookies. Any ideas?

services.AddIdentity<ApplicationUser, IdentityRole>(config =>
{
    //  Require a confirmed email in order to log in
    config.SignIn.RequireConfirmedEmail = true;
})
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultTokenProviders();

app.UseIdentity();

//  Add cookie middleware to the configure an identity request and persist it to a cookie.
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationScheme = "Cookie",
    LoginPath = new PathString("/Account/Login/"),
    AccessDeniedPath = new PathString("/Account/Forbidden/"),
    AutomaticAuthenticate = true,
    AutomaticChallenge = true,
    ExpireTimeSpan = TimeSpan.FromMinutes(20),
    SlidingExpiration = true,

});

I also have some razor code that controls whether to show the admin menu on the _layout page. This crashes when the cookie expires as the users suddenly has no claims. Is there a better way to handle this?

// If user is admin then show drop down with admin navigation
@if (User.HasClaim(System.Security.Claims.ClaimTypes.Role, "admin"))
{
    <ul class="nav navbar-nav">
        @*etc*@
    </ul>
}
Tailrace answered 12/6, 2017 at 13:18 Comment(2)
could you also post your AddIdentity section block?Shcherbakov
I have updated my question with the AddIdentity block from ConfigureServices.Tailrace
S
4

You do not need a separate CookieAuthentication middleware when you are using ASPNET identity. UseIdentity() will do that for you and generate a cookie. You can set the "cookie options" in the AddIdentity block of the application like so:

     services.AddIdentity<ApplicationUser, IdentityRole>(config =>
            {
                //  Require a confirmed email in order to log in
                config.SignIn.RequireConfirmedEmail = true;

               // Your Cookie settings
              config.Cookies.ApplicationCookie.ExpireTimeSpan = TimeSpan.FromDays(1);
              config.Cookies.ApplicationCookie.LoginPath = "/Account/LogIn";
              config.Cookies.ApplicationCookie.LogoutPath = "/Account/LogOut";
            }).AddEntityFrameworkStores<ApplicationDbContext().AddDefaultTokenProviders();

Also, take a look at https://mcmap.net/q/366787/-asp-net-core-mvc-setting-expiration-of-identity-cookie, it gives a background of this sort of a scenario with a very good explanation.

Shcherbakov answered 12/6, 2017 at 15:21 Comment(5)
Thanks @Muqeet. Your response helped me to solve my problem!Tailrace
@muqeetKhan In this new ASP.NET article they are using the following to setup the options. What is the difference in your approach and the article's approach, do you know? // Configure Identity services.Configure<IdentityOptions>(options => {.....}Cambrai
@Cambrai just different extension methods. In my example, I am using the extension method that takes the options object which is then configured in DI within the method. In the link you gave, they are just doing it separately. Its open source yay! look here on githubShcherbakov
Either I am blind, or in 3.1 config.Cookies is gone. How should I configure the LoginPath now?Thermodynamic
It's been moved to services.ConfigureApplicationCookie(configure => { ... });Tansey
T
0

I think the problem was that I was persisting data to a cookie with different settings.

Not sure if it's the proper way to do it, but I was able to solve the problem by using both services.AddIdentity and app.UseCookieAuthentication as below.

In ConfigureServices, set the cookie for log in:

        //  set the cookie for sign in
        services.AddIdentity<ApplicationUser, IdentityRole>(config =>
        {               
            //  Require a confirmed email in order to log in
            config.SignIn.RequireConfirmedEmail = true;
            // Cookie settings
            config.Cookies.ApplicationCookie.ExpireTimeSpan = TimeSpan.FromHours(10);
            config.Cookies.ApplicationCookie.LoginPath = "/Account/LogIn";
            config.Cookies.ApplicationCookie.LogoutPath = "/Account/LogOut";
        }).AddEntityFrameworkStores<ApplicationDbContext>().AddDefaultTokenProviders();

In Configure set the cookie scheme used to persist claims:

        //  Add cookie middleware to the configure an identity request and persist it to a cookie.
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationScheme = "Cookie",
            LoginPath = new PathString("/Account/Login/"),
            AccessDeniedPath = new PathString("/Account/Forbidden/"),
            AutomaticAuthenticate = true,
            AutomaticChallenge = true,
            //ExpireTimeSpan = TimeSpan.FromSeconds(10),
            ExpireTimeSpan = TimeSpan.FromHours(10),
            SlidingExpiration = true,
        });

In the log in method, persist the claims:

await HttpContext.Authentication.SignInAsync("Cookie", userPrincipal);
Tailrace answered 12/6, 2017 at 17:21 Comment(0)
Z
0

I personally use this way in order to use SignInManager in future:

    builder.Services.AddDbContext<Server04DbContext>(options =>
    {
        options.UseSqlServer(builder.Configuration.GetConnectionString("Local"));
    }).AddIdentity<AppUser, IdentityRole>(options =>
    {
        options.SignIn.RequireConfirmedEmail = false;
        options.User.RequireUniqueEmail = false;
        options.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyz0123456789";
        options.Lockout.MaxFailedAccessAttempts = 5;
        options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(10);
        options.Password.RequireNonAlphanumeric = false;
        options.Password.RequiredLength = 4;
    }).AddDefaultTokenProviders().AddEntityFrameworkStores<Server04DbContext>();

    builder.Services.ConfigureApplicationCookie(options =>
    {
        options.LoginPath = new PathString("/Auth/Login");
        options.LogoutPath = new PathString("/Auth/Logout");
        options.AccessDeniedPath = new PathString("/Home/AccessDenied");

        options.Cookie = new()
        {
            Name = "IdentityCookie",
            HttpOnly = true,
            SameSite = SameSiteMode.Lax,
            SecurePolicy = CookieSecurePolicy.Always
        };
        options.SlidingExpiration = true;
        options.ExpireTimeSpan = TimeSpan.FromDays(30);
    });
Zaragoza answered 17/1 at 5:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.