使用IdentityServer3进行双因素身份验证 - 记住浏览器

时间:2016-04-05 22:27:29

标签: identityserver3 two-factor-authentication

我使用IdentityServer3 + Asp.Net Identity(2.2.1)实现2fa。我坚持2fa实施。我看过" AspNetIdentity_2fa"样本,这有很大的帮助。

除了指示浏览器已成功通过身份验证的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页面/流程,与样本中的部分登录逻辑相结合。

2 个答案:

答案 0 :(得分: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);
    }
}

答案 1 :(得分:0)

我为记住浏览器要求实现了相同的操作,但是当我们注销并再次登录时,后面的语句返回总是为null。所以不跳过twofactory步骤..

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