>我想使用双因素身份验证来发出访问令牌. (如果用户已选择启用双因素身份验证)
>我想使用ASP.NET身份本身创建OTP代码. (就像我们在MVC Web应用程序中所做的那样SignInManager.SendTwoFactorCodeAsync(“电话代码”))
我当前实现的问题是,当我调用SignInManager.SendTwoFactorCodeAsync(“电话代码”)时,我得到错误用户ID未找到.
为了调试,我尝试调用User.Identity.GetUserId();并返回正确的用户ID.
我检查了Microsoft.AspNet.Identity.Owin程序集的源代码
public virtual async Task<bool> SendTwoFactorCodeAsync(string provider) { var userId = await GetVerifiedUserIdAsync().WithCurrentCulture(); if (userId == null) { return false; } var token = await UserManager.GenerateTwoFactorTokenAsync(userId,provider).WithCurrentCulture(); // See IdentityConfig.cs to plug in Email/SMS services to actually send the code await UserManager.NotifyTwoFactorTokenAsync(userId,provider,token).WithCurrentCulture(); return true; } public async Task<TKey> GetVerifiedUserIdAsync() { var result = await AuthenticationManager.AuthenticateAsync(DefaultAuthenticationTypes.TwoFactorCookie).WithCurrentCulture(); if (result != null && result.Identity != null && !String.IsNullOrEmpty(result.Identity.GetUserId())) { return ConvertIdFromString(result.Identity.GetUserId()); } return default(TKey); }
从上面的代码可以看出,SendTwoFactorCodeAsync方法在内部调用GetVerifiedUserIdAsync,它检查双因素身份验证cookie.由于这是一个web api项目,因此不存在cookie并返回0,导致找不到用户id错误.
我的问题,如何使用asp.net身份在web api中正确实现双因素身份验证?
解决方法
1.ApplicationOAuthProvider
在GrantResourceOwnerCredentials方法中,您必须添加此代码
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>(); ApplicationUser user = await userManager.FindAsync(context.UserName,context.Password); var twoFactorEnabled = await userManager.GetTwoFactorEnabledAsync(user.Id); if (twoFactorEnabled) { var code = await userManager.GenerateTwoFactorTokenAsync(user.Id,"PhoneCode"); IdentityResult notificationResult = await userManager.NotifyTwoFactorTokenAsync(user.Id,"PhoneCode",code); if(!notificationResult.Succeeded){ //you can add your own validation here context.SetError(error,"Failed to send OTP"); } } // commented for clarification ClaimIdentity oAuthIdentity ..... // Commented for clarification AuthenticationProperties properties = CreateProperties(user); // Commented for clarification
在CreateProperties方法内部用userObject替换参数,如下所示:
public static AuthenticationProperties CreateProperties(ApplicationUser user) { IDictionary<string,string> data = new Dictionary<string,string> { { "userId",user.Id },{ "requireOTP",user.TwoFactorEnabled.ToString() },} // commented for clarification }
以上代码检查用户是否启用了TFA,如果启用它将生成验证码并使用您选择的SMSService发送.
2.创建TwoFactorAuthorize属性
创建响应类ResponseData
public class ResponseData { public int Code { get; set; } public string Message { get; set; } }
添加TwoFactorAuthorizeAttribute
public override async Task OnAuthorizationAsync(HttpActionContext actionContext,System.Threading.CancellationToken cancellationToken) { #region Get userManager var userManager = HttpContext.Current.GetOwinContext().Get<ApplicationUserManager>(); if(userManager == null) { actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized,new ResponseData { Code = 100,Message = "Failed to authenticate user." }); return; } #endregion var principal = actionContext.RequestContext.Principal as ClaimsPrincipal; #region Get current user var user = await userManager.FindByNameAsync(principal?.Identity?.Name); if(user == null) { actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized,Message = "Failed to authenticate user." }); return; } #endregion #region Validate Two-Factor Authentication if (user.TwoFactorEnabled) { actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized,new ResponseData { Code = 101,Message = "User must be authenticated using Two-Factor Authentication." }); } #endregion return; } }
3.使用TwoFactorAuthorizeAttribute
在控制器中使用TwoFactorAuthorizeAttribute
[Authorize] [TwoFactorAuthorize] public IHttpActionResult DoMagic(){ }
4.验证OTP
在您的AccountController中,您必须添加api端点以验证OTP
[Authorize] [HttpGet] [Route("VerifyPhoneOTP/{code}")] public async Task<IHttpActionResult> VerifyPhoneOTP(string code) { try { bool verified = await UserManager.VerifyTwoFactorTokenAsync(User.Identity.GetUserId(),code); if (!verified) return BadRequest($"{code} is not a valid OTP,please verify and try again."); var result = await UserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId(),false); if (!result.Succeeded) { foreach (string error in result.Errors) errors.Add(error); return BadRequest(errors[0]); } return Ok("OTP verified successfully."); } catch (Exception exception) { // Log error here } }