具有2FA的Asp.NET标识-记住会话后不保留浏览器Cookie

lrl1mhuk  于 2023-03-13  发布在  .NET
关注(0)|答案(4)|浏览(159)

我使用的是最新的MVC5.2示例代码,带有Asp.Identity和双因素身份验证。
启用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代码,如果他们已经保存它。
有人看过这个吗,还是我错过了什么?

f8rj6qna

f8rj6qna1#

这段代码看起来不像是设计用来在同一个请求/响应中设置多个身份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");
                }
            }
        });

确保设置与主Asp.NET标识Cookie相同的ExpireTimeSpan和SldingExpiration以保留这些设置(因为它们合并在AuthenticationResponseGrant中)。

fafcakar

fafcakar2#

这似乎仍然是Identity 2.2.1中的一个问题(它可能在Asp.NET Identity 3.0中得到修复-但目前是预发布版本,需要更新版本的.Net framework 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);
balp4ylt

balp4ylt3#

你可以做的是分配你自己的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);
    }
}

这是你将得到的:

对我来说是这样。

b1zrtrql

b1zrtrql4#

补充这里丰富的信息;我们使用的是v2.2.1(.Core,.EntityFramework & .OWIN包),我们需要为“(双因素)记住浏览器”cookie自定义“ExpireTimeSpan”,所以我添加了以下内容:
`public static class MyAppBuilderExtensions { public static void UseTwoFactorRememberBrowserCookie( this IAppBuilder app, string authenticationType, TimeSpan expireTimeSpan, bool? slidingExpiration = null) { if (app == null) { throw new ArgumentNullException(nameof(app)); }

var cookieAuthOpts =
            new CookieAuthenticationOptions
            {
                // E.g.  'DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie'
                AuthenticationType = authenticationType,
                AuthenticationMode = AuthenticationMode.Passive,
                CookieName = CookiePrefixConst + authenticationType,
                ExpireTimeSpan = expireTimeSpan
            };

        if (slidingExpiration.HasValue)
        {
            // Appears to (currently) be 'True' by default
            cookieAuthOpts.SlidingExpiration = slidingExpiration.Value;
        }

        app.UseCookieAuthentication(cookieAuthOpts);
    }

    private const string CookiePrefixConst = ".AspNet.";
}

`
例如,我这样调用/调用它(-替换对Microsoft的调用):

// Original/Microsoft's:
//app.UseTwoFactorRememberBrowserCookie(
//    DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);

// Mine:
app.UseTwoFactorRememberBrowserCookie(
        DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie,
        TimeSpan.FromMinutes(2));

但我没有看到“TwoFactorRememberBrowser”(/'DefaultAuthenticationTypes. TwoFactorRememberBrowserCookie')Cookie的其他标识;例如,我尝试了这个(在我们的MVC控制器中),并检查了test/“ts”变量-只有“ApplicationCookie”变量有值:
`// We can see from the 'authTypes' variable value that the App-Builder 'Use...()' method has 'hook'ed/plugged-in the 'TwoFactorRememberBrowser' middleware (to the authentication chain) var authTypes = SignInManager.AuthenticationManager.GetAuthenticationTypes();

// 'TwoFactorRememberBrowser'
var ts1 =
    await SignInManager.AuthenticationManager.AuthenticateAsync(
            DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);

// 'TwoFactorCookie'
var ts2 =
    await SignInManager.AuthenticationManager.AuthenticateAsync(
            DefaultAuthenticationTypes.TwoFactorCookie);

// 'ApplicationCookie'
var ts3 =
    await SignInManager.AuthenticationManager.AuthenticateAsync(
            DefaultAuthenticationTypes.ApplicationCookie);

// Bearer
var ts4 =
    await SignInManager.AuthenticationManager.AuthenticateAsync(
            @"Bearer");

var tfaBrowserRemembered =
    SignInManager.AuthenticationManager.TwoFactorBrowserRemembered(
        User.Identity.GetUserId());

Debug.WriteLine(
    $"DBG:  2FA Br R = '{tfaBrowserRemembered}'");

`
即使查看登录管理器的身份验证管理器的“AuthenticationResponseGrant”(-“SignInManager.AuthenticationManager.AuthenticationResponseGrant. Principal ')中的”主体“所包含的身份,也 * 只有 * 一个身份-仅用于”应用程序Cookie“。
但在我的案例中,我发现有人在“VerifyCode”视图(cshtml)中出错了,因此“RememberBrowser”值没有得到正确维护(/捕获),即对“TwoFactorSignInAsync()”方法的调用被赋予了一个(默认/未初始化)值“*False”。
修复此问题后,确保“RememberBrowser”的"True“值通过/传递给”TwoFactorSignInAsync()“方法调用,一切似乎都按预期工作。
因此,首先/也值得检查这个(更简单的)问题。

相关问题