2016-04-05 55 views
0

我使用IdentityServer3 + Asp.Net身份(2.2.1)实现2fa身份验证。我被困在2fa实现上。我看了一下“AspNetIdentity_2fa”示例,这非常有帮助。IdentityServer3身份验证的两个因素 - 记住浏览器

我已将所有东西连接起来,除了cookie指示浏览器已被成功验证。我可以在代码确认期间设置cookie,但是我无法在PostAuthenticateLocalAsync()调用中访问cookie以查看是否采用2fa路径。

 protected override Task<AuthenticateResult> PostAuthenticateLocalAsync(User user, SignInMessage message) 
    { 
     if (user.TwoFactorEnabled) // && !TwoFactorCookieSet... 
     { 
      return Task.FromResult(new AuthenticateResult("/auth/sendcode", user.Id, user.DisplayName)); 
     } 

     return base.PostAuthenticateLocalAsync(user, message); 
    } 

我相信我采用正确的方法使用部分登录,但是如何检测到当前浏览器已被批准?

更多详细信息:/ auth/sendcode是2fa的标准Asp.Net Identity页面/流程,与样本的部分登录逻辑相结合。

回答

0

好吧,我发现OwinEnvironmentService可以注入到IdentityServer服务中。我可以通过OwinEnvironmentService获取cookie。我很想听听你对这个解决方案的任何意见(这并不意味着要生产准备,它只是一个概念):

internal class UserService : AspNetIdentityUserService<User, string> 
{ 
    private readonly OwinEnvironmentService _owinEnvironmentService; 

    public UserService(UserManager userMgr, OwinEnvironmentService owinEnvironmentService) : base(userMgr) 
    { 
     _owinEnvironmentService = owinEnvironmentService; 
     DisplayNameClaimType = IdentityServer3.Core.Constants.ClaimTypes.Name; 
    } 

    protected override Task<AuthenticateResult> PostAuthenticateLocalAsync(User user, SignInMessage message) 
    { 
     if (user.TwoFactorEnabled) 
     { 
      var twoFactorNeeded = false; 
      object httpContext; 

      if (_owinEnvironmentService.Environment.TryGetValue("System.Web.HttpContextBase", out httpContext)) 
      { 
       var cookies = (httpContext as HttpContext)?.Request.Cookies; 
       if (cookies != null && !cookies.AllKeys.Contains(IdentityConstants.CookieNames.TwoFactorCompleted)) twoFactorNeeded = true; 
      } 

      if (twoFactorNeeded) 
       return Task.FromResult(new AuthenticateResult("/auth/sendcode", user.Id, user.DisplayName)); 
     } 

     return base.PostAuthenticateLocalAsync(user, message); 
    } 
} 

修订

基于Brock的评论,我认为我有更好的解决方案。

// custom User Service 
internal class UserService : AspNetIdentityUserService<User, string> 
{ 
    private readonly OwinEnvironmentService _owinEnvironmentService; 

    public UserService(UserManager userMgr, OwinEnvironmentService owinEnvironmentService) : base(userMgr) 
    { 
     _owinEnvironmentService = owinEnvironmentService; 
     DisplayNameClaimType = IdentityServer3.Core.Constants.ClaimTypes.Name; 
    } 

    protected override async Task<AuthenticateResult> PostAuthenticateLocalAsync(User user, SignInMessage message) 
    { 
     if (user.TwoFactorEnabled) 
     { 
      var owinContext = new OwinContext(_owinEnvironmentService.Environment); 
      var result = await owinContext.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie); 
      if(result == null) return new AuthenticateResult("/auth/sendcode", user.Id, user.DisplayName); 
     } 

     return await base.PostAuthenticateLocalAsync(user, message); 
    } 
} 

// (in MVC controller) generate the 2FA security code and send it 
public async Task<ActionResult> SendCode(SendCodeViewModel model) 
{ 
    // ...some code removed for brevity... 

    var token = await UserManager.GenerateTwoFactorTokenAsync(userId, model.SelectedProvider); 
    var identityResult = await UserManager.NotifyTwoFactorTokenAsync(userId, model.SelectedProvider, token); 
    if (!identityResult.Succeeded) return View("Error"); 

    return RedirectToAction("VerifyCode", new { Provider = model.SelectedProvider, model.ReturnUrl, model.RememberMe }); 
} 

// (in MVC controller) verify the code and sign in with 2FA 
public async Task<ActionResult> VerifyCode(VerifyCodeViewModel model) 
{ 
    // ...some code removed for brevity... 

    var signInManager = new SignInManager<User, string>(UserManager, Request.GetOwinContext().Authentication); 
    if (await UserManager.VerifyTwoFactorTokenAsync(user.Id, model.Provider, model.Code)) 
    { 
     await UserManager.ResetAccessFailedCountAsync(user.Id); 
     await signInManager.SignInAsync(user, model.RememberMe, model.RememberBrowser); 
     var resumeUrl = await env.GetPartialLoginResumeUrlAsync(); 
     return Redirect(resumeUrl); 
    } 
    else 
    { 
     await UserManager.AccessFailedAsync(user.Id); 
     ModelState.AddModelError("", "Invalid code."); 
     return View(model); 
    } 
} 
+0

为什么不叫身份验证的IOwinContext的2fa cookie?这已经有一段时间了 - 我忘记了AspId管道如何表面2fa cookie。他们现在不使用登录管理器吗? –

+0

是的,他们确实使用SignInManager。我一直在避免它,因为我认为它不适合IdentityServer3。我会试一试,然后回来做笔记。 – Kevin

+0

好的,根据您的评论(谢谢!),我有一个更好的解决方案,我将作为答案发布。它使用UserManager和SignInManager的组合。 – Kevin

0

我实现了浏览器记住要求一样的,但是下面的语句返回总是空的时候,我们注销和登录again.so twofactory步骤没有跳过..

var result = await owinContext.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie); 
+0

您是否找到了解决方案? –