How to change the cookie ExpireTimeSpan in Asp.Net Identity after ConfigureAuth

故事扮演 提交于 2019-12-05 11:16:12

This took some time to figure out, but I believe I have found the correct solution. It's funny because I already had the solution, I just didn't explore the options available in the scope.

CookieAuthenticationOptions provides only one hook, a delegate property OnValidateIdentity.

The OnValidateIdentity occurs each time someone logs in(via cookie auth provider), which happens to be the perfect time to run some custom logic that determines their new Expiration time. It also lets you configure it globally.

I've debugged it and can confirm the value for the configuration option sticks, and remains for future logins globally until app pool recycle. And also that the value override for expiration applies to the authenticated user after signin callback.

So it's up to you what logic determines the value, and whether you want to set that on a per-person level, or global level.

Here is the code sample to help paint a better picture of this..

public void ConfigureAuth(IAppBuilder app) {
    app.CreatePerOwinContext(ApplicationDbContext.Create);
    app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
    app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
    app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
    app.CreatePerOwinContext<ApplicationGroupManager>(ApplicationGroupManager.Create);
    app.CreatePerOwinContext<ApplicationDepartmentManager>(ApplicationDepartmentManager.Create);

    app.UseCookieAuthentication(new CookieAuthenticationOptions {
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        LoginPath = new PathString("/Login"),
        Provider = new CookieAuthenticationProvider {

            OnValidateIdentity = delegate(CookieValidateIdentityContext context) {
                var stampValidator = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser, int>(
                    validateInterval: TimeSpan.FromMinutes(15),
                    regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
                    getUserIdCallback: (id) => (id.GetUserId<int>())
                );
                Task result = stampValidator.Invoke(context);

                bool found_custom_expiration = false;
                int timeout = STATIC_CONFIG.DefaultSessionExpireMinutes;
                DBHelper.Query(delegate (SqlCommand cmd) {
                    cmd.CommandText = @"
                        SELECT [value] 
                        FROM [dbo].[Vars] 
                        WHERE [FK_Department] = (
                            SELECT [Id] FROM [dbo].[ApplicationDepartments] WHERE [title] = 'Default'
                        ) 
                        AND [name]='Session Timeout'
                    ";
                    return cmd;
                }, delegate (SqlDataReader reader) {
                    timeout = reader["value"].ToInt32();
                    found_custom_expiration = true;
                    return false;
                });
                if (found_custom_expiration) {
                    // set it at GLOBAL level for all users.
                    context.Options.ExpireTimeSpan = TimeSpan.FromMinutes(timeout);
                    // set it for the current user only.
                    context.Properties.ExpiresUtc = context.Properties.IssuedUtc.Value.AddMinutes(timeout);
                }
                // store it in a claim, so we can grab the remaining time later.
                // credit: https://stackoverflow.com/questions/23090706/how-to-know-when-owin-cookie-will-expire
                var expireUtc = context.Properties.ExpiresUtc;
                var claimType = "ExpireUtc";
                var identity = context.Identity;
                if (identity.HasClaim(c => c.Type == claimType)) {
                    var existingClaim = identity.FindFirst(claimType);
                    identity.RemoveClaim(existingClaim);
                }
                var newClaim = new System.Security.Claims.Claim(claimType, expireUtc.Value.UtcTicks.ToString());
                context.Identity.AddClaim(newClaim);
                return result;
            }
        },
        SlidingExpiration = true,
        // here's the default global config which was seemingly unchangeable.. 
        ExpireTimeSpan = TimeSpan.FromMinutes(STATIC_CONFIG.DefaultSessionExpireMinutes)
    });

    ApplicationHandler.OwinStartupCompleted();

}

That could definitely be improved to issue the change only when not already changed. And you don't have to use a database query, it could be an XML read, or web.config appsetting, or whatever. The identity is in place... So you could even access the context.Identity and perform some kind of custom check on a ApplicationUser .

So... the relevant bit...

OnValidateIdentity = delegate(CookieValidateIdentityContext context) {
            var stampValidator = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser, int>(
                validateInterval: TimeSpan.FromMinutes(30),
                regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
                getUserIdCallback: (id) => (id.GetUserId<int>())
            );
            Task result = stampValidator.Invoke(context);

            int my_custom_minutes = 60; // run your logic here.
                // set it at GLOBAL level for all (future) users.
                context.Options.ExpireTimeSpan = TimeSpan.FromMinutes( my_custom_minutes );
                // set it for the current user only.
                context.Properties.ExpiresUtc = context.Properties.IssuedUtc.Value.AddMinutes( my_custom_minutes );

            return result;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!