如何更改“.AspNetCore.Identity.Application”cookie 过期时间?

How to change ".AspNetCore.Identity.Application" cookie expiration?

我正在使用 ASP.NET Core 与 Identity Server 和 Open Id Connect,如 here 所述。我需要在设置记住我选项时更改身份验证 cookie 过期时间(默认为 14 天)。我可以看到名为“.AspNetCore.Identity.Application”的 cookie 对此负责。我正在尝试像这样设置到期时间:

.AddCookie(options =>
{
    options.Cookie.Expiration = TimeSpan.FromDays(1);
    options.ExpireTimeSpan = TimeSpan.FromDays(1);
})

但是它影响了另一个名为“.AspNetCore.Cookies”的cookie(包含相同的令牌值),它具有Session 过期并且似乎没有做任何事情。我发现所有更改过期的方法都只修改“.AspNetCore.Cookies”cookie,我找不到任何方法来修改“.AspNetCore.Identity.Application”cookie。 (顺便说一下,出于某种原因,services.ConfigureApplicationCookie 方法根本没有为我触发)。

任何人都可以解释一下这两个 cookie 之间的区别是什么以及我如何修改“.AspNetCore.Identity.Application”过期时间?

我的代码在Startup.ConfigureServices

services.AddMvc(options =>
{
    // ...
})

services.AddAuthorization(options =>
{
    options.AddPolicy(PolicyNames.UserPolicy, policyBuilder =>
    {
        // ... 
    });
});

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = "oidc";
})
.AddCookie(options =>
{
    options.AccessDeniedPath = "/AccessDenied";
    options.SlidingExpiration = true;
})
.AddOpenIdConnect("oidc", options =>
{
    options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.Authority = "<authority>";
    options.RequireHttpsMetadata = false;
    options.ClientId = "<id>";
    options.ClientSecret = "<secret>";
    options.ResponseType = "code id_token";
    options.SaveTokens = true;
    options.GetClaimsFromUserInfoEndpoint = true;
    // ...
});

services.ConfigureApplicationCookie(options =>
{
    options.Cookie.Name = "MyCookie";
    options.Cookie.Expiration = TimeSpan.FromDays(1);
    options.ExpireTimeSpan = TimeSpan.FromDays(1);
});

正如 Kirk Larkin 所说,“.AspNetCore.Identity.Application”cookie 可能是由使用 Asp.Net Identity 的 Identity Server 应用程序设置的。 因此,如果您想在 IS4 应用程序上管理用户会话,您需要在那里进行配置。

IS4 应用程序:“.AspNetCore.Identity.Application”cookie。

如果您使用 Identity 将 cookie 配置为持久性,则需要在用户登录时设置过期时间。

var props = new AuthenticationProperties {
  IsPersistent = true,
  ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration)
};
await HttpContext.SignInAsync(userId, userName, props);

如果您不设置 IsPersistent=true,则 cookie 具有会话生命周期,您可以像这样设置包含的身份验证票证到期时间:

.AddCookie(options => {
    options.Cookie.Name = "idsrv_identity";
    options.ExpireTimeSpan = TimeSpan.FromHours(8);
    options.SlidingExpiration = true;
  });

您的客户端应用程序::“.AspNetCore.Cookies”cookie。

services.ConfigureApplicationCookie 不会被调用,因为如果您使用 .AddCookie(...) 则优先。选项是一样的。

这会将应用程序 cookie 设置为 会话

.AddCookie(options => {
    options.Cookie.Name = "myappcookie";
    options.ExpireTimeSpan = TimeSpan.FromHours(8);
    options.SlidingExpiration = true;
  });

使用 OIDC 使应用程序 cookie 持久化的一种方法是在 AddCookie 中的 OnSigningIn 事件中设置过期时间。

options.Events.OnSigningIn = (context) =>
{
    context.CookieOptions.Expires = DateTimeOffset.UtcNow.AddDays(30);
    return Task.CompletedTask;
};

关于用户会话的说明。

每种情况都不同,因此没有最佳解决方案,但请记住,您必须处理两个用户会话。一个在 IS4 应用程序上,一个在您的客户端应用程序上。这些可能会不同步。您需要考虑客户端应用程序上的持久用户会话是否有意义。当中央 SSO(单点登录)会话过期时,您不希望您的用户仍然登录您的客户端应用程序。

services.AddAuthentication 之前添加这一行对我最终对 IS4 有用,取自 this github issue:

services.ConfigureApplicationCookie(x =>
{
       x.ExpireTimeSpan = TimeSpan.FromDays(1);
});

我遵循 Github aspnetcore 来源的示例 AuthSamples.Cookies

    public void ConfigureServices(IServiceCollection services)
    {

...

        // Example of how to customize a particular instance of cookie options and
        // is able to also use other services.
        services.AddSingleton<IConfigureOptions<CookieAuthenticationOptions>, ConfigureMyCookie>();
    }

internal class ConfigureMyCookie : IConfigureNamedOptions<CookieAuthenticationOptions>
{
    // You can inject services here
    public ConfigureMyCookie()
    {
    }

    public void Configure(string name, CookieAuthenticationOptions options)
    {
        // Identityserver comes with two cookies:

        // Identity.Application
        // Identity.External

        // you can change the options here
        {
            options.ExpireTimeSpan = TimeSpan.FromHours(8);
        }
    }

    public void Configure(CookieAuthenticationOptions options)
        => Configure(Options.DefaultName, options);
}

在对 AspNetCore 3.1IdentityServer 4.0.4 存储库进行加扰后, 我找到了设置默认身份验证 cookie 选项的工作方式。

TD;LR:

    // in Startup.ConfigureService(IServiceCollection services)
    services.PostConfigure<CookieAuthenticationOptions>(IdentityConstants.ApplicationScheme, option =>
    {
        option.Cookie.Name = "Hello"; // change cookie name
        option.ExpireTimeSpan = TimeSpan.FromSeconds(30); // change cookie expire time span
    });

完整设置:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllersWithViews();
        services.AddRazorPages();

        // cookie policy to deal with temporary browser incompatibilities
        services.AddSameSiteCookiePolicy();
        services.AddDefaultAllowAllCors();

        // setting up dbcontext for stores;
        services.AddDbContext<ApplicationDbContext>(ConfigureDbContext);

        services
            .AddIdentity<ApplicationUser, IdentityRole>(options =>
            {
                options.SignIn.RequireConfirmedAccount = true;
            })
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultUI()
            .AddDefaultTokenProviders();

        // read clients from 
        var builder = services.AddIdentityServer(options =>
        {
            options.Events.RaiseSuccessEvents = true;
            options.Events.RaiseFailureEvents = true;
            options.Events.RaiseErrorEvents = true;
            options.Events.RaiseInformationEvents = true;
            options.UserInteraction.LoginUrl = "/identity/account/login";
            options.IssuerUri = _configuration.GetValue<string>("IdentityServer:IssuerUri");
        })

            .AddAspNetIdentity<ApplicationUser>()
            .AddDeveloperSigningCredential()
            .AddConfigurationStore<ApplicationConfigurationDbContext>(option => option.ConfigureDbContext = ConfigureDbContext)
            .AddOperationalStore<ApplicationPersistedGrantDbContext>(option => { option.ConfigureDbContext = ConfigureDbContext; })
            .AddJwtBearerClientAuthentication()
            .AddProfileService<ApplicationUserProfileService>();

        services.PostConfigure<CookieAuthenticationOptions>(IdentityConstants.ApplicationScheme, option =>
        {
            option.Cookie.Name = "Hello";
            option.ExpireTimeSpan = TimeSpan.FromSeconds(30);
        });
        services.AddScoped<Microsoft.AspNetCore.Identity.UI.Services.IEmailSender, EmailSender>();
        services.Configure<SmsOption>(_configuration);
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        // use this for persisted grants store
        InitializeDatabase(app);

        app.UseForwardedHeaders(new ForwardedHeadersOptions
        {
            ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
        });

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        app.UseDefaultAllowAllCors();

        app.UseStaticFiles();

        app.UseRouting();

        app.UseIdentityServer();

        app.UseAuthorization();


        app.UseStatusCodePages(async context =>

        {
            var response = context.HttpContext.Response;

            if (response.StatusCode == StatusCodes.Status401Unauthorized ||
                response.StatusCode == StatusCodes.Status403Forbidden)
                response.Redirect("/identity/account/login");

            if (context.HttpContext.Request.Method == "Get" && response.StatusCode == StatusCodes.Status404NotFound)
            {
                response.Redirect("/index");
            }
        });

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapDefaultControllerRoute();
            endpoints.MapRazorPages();
        });
    }