使用2FA的Asp.Net标识 - 记住会话后不保留浏览器cookie

时间:2015-02-26 14:34:41

标签: asp.net session cookies asp.net-identity

我正在使用带有Asp.Identity和双因素身份验证的MVC5.2的最新示例代码。

启用2FA后,当用户登录时,会提示输入代码(通过电话或电子邮件发送),并且他们可以选择“记住浏览器” - 这样他们就不会在该浏览器上再次询问代码

这在VerifyCode操作

中处理
var result = await SignInManager.TwoFactorSignInAsync(model.Provider, model.Code, isPersistent:  model.RememberMe, rememberBrowser: model.RememberBrowser);

请注意,model.RememberMe未在默认模板中使用,因此它为false。

我发现当我这样做时.AspNet.TwoFactorRememberBrowser被设置,会话结束时到期(所以它不记得浏览器)

现在,如果我设置isPersistent = true.AspNet.TwoFactorRememberBrowser会过期30天,这很好,但.AspNet.ApplicationCookie也会过期30天 - 这意味着当我关闭浏览器时并重新打开,我自动登录。

我想要它,以便它不会持续我的登录,但它将坚持我选择记住2FA代码。即用户应始终必须登录,但如果已经保存,则不应要求他们提供2fa代码。

有没有人看过这个,或者我错过了什么?

3 个答案:

答案 0 :(得分:12)

此代码似乎不是设计为在同一请求/响应中设置多个身份cookie,因为OWIN cookie处理程序最终共享相同的AuthenticationProperties。这是因为AuthenticationResponseGrant具有单个主体,但主体可以具有多个身份。

您可以通过在特定于2FA Cookie提供程序的ResponseSignIn和ResponseSignedIn事件中更改然后还原AuthenticationProperties来解决此错误:

        //Don't use this.
        //app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);

        //Set the 2FA cookie expiration and persistence directly
        //ExpireTimeSpan and SlidingExpiration should match the Asp.Net Identity cookie setting
        app.UseCookieAuthentication(new CookieAuthenticationOptions()
        {
            AuthenticationType = DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie,
            AuthenticationMode = AuthenticationMode.Passive,
            CookieName = DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie,
            ExpireTimeSpan = TimeSpan.FromHours(2),
            SlidingExpiration = true,
            Provider = new CookieAuthenticationProvider
            {
                OnResponseSignIn = ctx =>
                {
                    ctx.OwinContext.Set("auth-prop-expires", ctx.Properties.ExpiresUtc);
                    ctx.OwinContext.Set("auth-prop-persist", ctx.Properties.IsPersistent);
                    var issued = ctx.Properties.IssuedUtc ?? DateTimeOffset.UtcNow;
                    ctx.Properties.ExpiresUtc = issued.AddDays(14);
                    ctx.Properties.IsPersistent = true;
                },
                OnResponseSignedIn = ctx =>
                {
                    ctx.Properties.ExpiresUtc = ctx.OwinContext.Get<DateTimeOffset?>("auth-prop-expires");
                    ctx.Properties.IsPersistent = ctx.OwinContext.Get<bool>("auth-prop-persist");
                }
            }
        });

确保将相同的ExpireTimeSpan和SldingExpiration设置为主要的Asp.Net Identity cookie以保留这些设置(因为它们在AuthenticationResponseGrant中合并)。

答案 1 :(得分:3)

这似乎仍然是Identity 2.2.1中的一个问题(它可以在Asp.Net Identity 3.0中修复 - 但目前已预发布,需要更高版本的.Net框架4.5)

以下解决方法现在似乎还可以: 使用错误的值在SignInManager.TwoFactorSignInAsync上设置cookie,因此在VerifyCode操作成功时,我将cookie重置为持久性并为其提供我希望的到期日期(在本例中我将其设置为一年)

  public async Task<ActionResult> VerifyCode(VerifyCodeViewModel model)
  {
        if (!ModelState.IsValid)
        {
            return View(model);
        }            var result = await SignInManager.TwoFactorSignInAsync(model.Provider, model.Code, isPersistent:  model.RememberMe, rememberBrowser: model.RememberBrowser);
        switch (result)
        {
            case SignInStatus.Success:
                // if we remember the browser, we need to adjsut the expiry date as persisted above
                // Also set the expiry date for the .AspNet.ApplicationCookie 
                if (model.RememberBrowser)
                {
                    var user = await UserManager.FindByIdAsync(await SignInManager.GetVerifiedUserIdAsync());
                    var rememberBrowserIdentity = AuthenticationManager.CreateTwoFactorRememberBrowserIdentity(user.Id);
                    AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTime.UtcNow.AddDays(365) }, rememberBrowserIdentity);
                }

                return RedirectToLocal(model.ReturnUrl);

答案 2 :(得分:0)

您可以做的是分配您自己的 CookieManager 类来修改 TwoFactorRememberBrowserCookie 的过期时间。这似乎比修改 Application_PostAuthenticateRequest 中的 cookie 更好。

这可以解决您可以保留所有身份验证 cookie 或不保留所有身份验证 cookie 的问题。

把它放在你的 ConfigureAuth 中,最后一行设置你的自定义 cookie 管理器。

public void ConfigureAuth(IAppBuilder app)
{  
    // left out all but the modified initialization of the TwoFactorRememberBrowserCookie

    var CookiePrefix = ".AspNet.";
    app.UseCookieAuthentication(new CookieAuthenticationOptions {
        AuthenticationType = DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie,
        AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Passive,
        CookieName = CookiePrefix + DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie,
        ExpireTimeSpan = TimeSpan.FromDays(14),
        CookieManager = new TwoFactorRememberBrowserCookieManager()
    });
}

仅将此 CookieManager 类用于 TwoFactorRememberBrowserCookie。 如果您不在 TwoFactorSignInAsync 中保留 cookie,遗憾的是 ExpirationTimeout 会被忽略。

所以只需在 CookieManager 中再次设置(这是来自 Microsoft.Owin.Infrastructure.CookieManager 的 cookie 管理器的修改版本):

public class TwoFactorRememberBrowserCookieManager : Microsoft.Owin.Infrastructure.ICookieManager
{
    string CookiePrefix = ".AspNet.";
    Microsoft.Owin.Infrastructure.ICookieManager cm = new Microsoft.Owin.Infrastructure.ChunkingCookieManager();
    public void AppendResponseCookie(IOwinContext context, string key, string value, CookieOptions options)
    {
        if (key == CookiePrefix + DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie) {
            options.Expires = DateTime.UtcNow.AddDays(14);
        }
        cm.AppendResponseCookie(context, key, value, options);
    }
    public void DeleteCookie(IOwinContext context, string key, CookieOptions options)
    {
        cm.DeleteCookie(context, key, options);
    }
    public string GetRequestCookie(IOwinContext context, string key)
    {
        return cm.GetRequestCookie(context, key);
    }
}

这就是你会得到的:

enter image description here

那样对我有用。