I\'m using the latest sample code for MVC5.2 with Asp.Identity and Two Factor authentication.
With 2FA enabled, when a user logins, the get prompted for a code (sent
This still appears to be an issue in Identity 2.2.1 (It may be fixed in Asp.Net Identity 3.0 - but that is currently pre-released and requires a later version of .Net framework that 4.5)
The following work around seems ok for now: The cookie is getting set on the SignInManager.TwoFactorSignInAsync with the wrong values, so on Success of the VerifyCode action, I reset the cookie to be persistent and give it the expiry date that I wish (in this case I set it to a year)
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);
It doesn't seem like this code was designed to set more than one identity cookie in the same request/response because the OWIN cookie handlers end up sharing the same AuthenticationProperties. This is because the AuthenticationResponseGrant has a single principal, but the principal can have multiple identities.
You can workaround this bug by altering and then restoring the AuthenticationProperties in the ResponseSignIn and ResponseSignedIn events specific to the 2FA cookie provider:
//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");
}
}
});
Make sure to set the same ExpireTimeSpan and SldingExpiration as your main Asp.Net Identity cookie to preserve those settings (since they get merged in the AuthenticationResponseGrant).