Authentication 如何使用ASP.NET标识在Web API 2中实现双因素身份验证?
我已经看到了这个关于如何在WebAPI中创建双因素身份验证的链接,但是我的要求没有什么不同Authentication 如何使用ASP.NET标识在Web API 2中实现双因素身份验证?,authentication,asp.net-web-api,asp.net-identity,one-time-password,Authentication,Asp.net Web Api,Asp.net Identity,One Time Password,我已经看到了这个关于如何在WebAPI中创建双因素身份验证的链接,但是我的要求没有什么不同 我想使用双因素身份验证来颁发访问令牌。(如果用户选择启用双因素身份验证) 我想使用ASP.NET标识本身创建OTP代码。(就像我们在MVC web应用程序中所做的那样SignInManager.sendtowfactorcodeasync(“电话代码”) 我当前实现的问题是,当我调用SignInManager.SendTwoFactorCodeAsync(“电话代码”)时,我得到了找不到用户id的错误信息
SignInManager.sendtowfactorcodeasync(“电话代码”)
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(字符串提供程序)
{
var userId=wait GetVerifiedUserIdAsync().WithCurrentCulture();
if(userId==null)
{
返回false;
}
var token=await UserManager.GenerateTwoFactorTokenAsync(userId,provider).WithCurrentCulture();
//请参阅IdentityConfig.cs以插入电子邮件/短信服务以实际发送代码
等待UserManager.NotifyTwoFactoryTokenAsync(用户ID、提供程序、令牌).WithCurrentCulture();
返回true;
}
公共异步任务GetVerifiedUserIdAsync()
{
var result=wait AuthenticationManager.authenticateSync(DefaultAuthenticationTypes.TwoFactorCookie).WithCurrentCulture();
if(result!=null&&result.Identity!=null&&String.IsNullOrEmpty(result.Identity.GetUserId())
{
返回ConvertIdFromString(result.Identity.GetUserId());
}
返回默认值(TKey);
}
从上面的代码中可以看出,SendTwoFactorCodeAsync
方法在内部调用检查双因素身份验证cookie的GetVerifiedUserIdAsync
。由于这是一个web api项目,cookie不存在,返回0,导致用户id未找到错误
我的问题是,如何使用asp.net标识在web api中正确实现双因素身份验证?这是我在api上实现此功能的方法。我假设您使用的是默认的asp.net单用户模板 1.ApplicationAuthProvider 在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
添加TwofactoraAuthorizeAttribute
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, new ResponseData
{
Code = 100,
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;
}
}
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(), "PhoneCode", 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
}
}
[授权]
[HttpGet]
[路由(“VerifyPhoneOTP/{code}”)]
公共异步任务VerifyPhoneOTP(字符串代码)
{
尝试
{
bool verified=wait UserManager.VerifyTwoFactorTokenAsync(User.Identity.GetUserId(),“PhoneCode”,code);
如果(!已验证)
return BadRequest($“{code}不是有效的OTP,请验证并重试。”);
var result=await UserManager.setTwoFactoryEnabledAsync(User.Identity.GetUserId(),false);
如果(!result.successed)
{
foreach(result.Errors中的字符串错误)
错误。添加(错误);
返回错误请求(错误[0]);
}
返回Ok(“OTP验证成功”);
}
捕获(异常)
{
//此处记录错误
}
}
Hi Spharah,非常感谢您提供的详细答案。您还可以包括验证用户输入的OTP代码的逻辑吗?Hi Anand,我更新了答案以包含验证OTP的代码,别忘了对答案进行投票:-)Spharah,我已经投票了。在接受答案之前,请先澄清疑问。OTP验证成功后,将IsTwoFactorEnabled设置为false。比方说,用户在另一台机器上再次登录,现在他将无法获得OTP(对吗?),因为IstwoFactoryEnabled设置为false。你什么时候重新启用它?Anand,你是对的,这是我正在开发的应用程序的要求。要在每次用户登录时持续要求用户输入OTP,您必须更改TwoFactorAuthorization代码。我会为这种情况准备解决方案。PEO,不,我没有。
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, new ResponseData
{
Code = 100,
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;
}
}
[Authorize]
[TwoFactorAuthorize]
public IHttpActionResult DoMagic(){
}
[Authorize]
[HttpGet]
[Route("VerifyPhoneOTP/{code}")]
public async Task<IHttpActionResult> VerifyPhoneOTP(string code)
{
try
{
bool verified = await UserManager.VerifyTwoFactorTokenAsync(User.Identity.GetUserId(), "PhoneCode", 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
}
}