2016-04-05 83 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

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