This is what I implemented to make this work on api. I assume that you are using the default ASP.NET single user template.
1. ApplicationOAuthProvider
inside the GrantResourceOwnerCredentials method you must add this code
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){
Inside the CreateProperties method, replace paramenter with userObject as follows:
public static AuthenticationProperties CreateProperties(ApplicationUser user) { IDictionary<string, string> data = new Dictionary<string, string> { { "userId", user.Id }, { "requireOTP" , user.TwoFactorEnabled.ToString() }, }
The above code will check if the TFA user is allowed, if enabled, he will generate a confirmation code and send it using SMSService of your choice.
2. Create an attribute TwoFactorAuthorize
create response class ResponseData strong>
public class ResponseData { public int Code { get; set; } public string Message { get; set; } }
add 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, 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; } }
3. Use the TwoFactorAuthorizeAttribute attribute
controller uses TwoFactorAuthorizeAttribute
[Authorize] [TwoFactorAuthorize] public IHttpActionResult DoMagic(){ }
4. Check OTP In your AccountController you must add an api endpoint to check 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) {