How to implement two factor auth in Web API 2 using ASP.NET identity?

别来无恙 提交于 2019-12-03 12:57:20

This is what i have implemented to get this working on an api. i assume you are using the default ASP.NET single user template.

1. ApplicationOAuthProvider

inside 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){
   //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

Inside CreateProperties method replace the paramenter with userObject like this:

public static AuthenticationProperties CreateProperties(ApplicationUser user)
{
  IDictionary<string, string> data = new Dictionary<string, string>
  {
    { "userId", user.Id },
    { "requireOTP" , user.TwoFactorEnabled.ToString() },
  }

// commented for clarification
}

The above code with check if the user has TFA enable, if its enabled it will generate verification code and send it using SMSService of your choice.

2. Create TwoFactorAuthorize Attribute

create response class ResponseData

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 TwoFactorAuthorizeAttribute

in a controller use TwoFactorAuthorizeAttribute

[Authorize]
[TwoFactorAuthorize]
public IHttpActionResult DoMagic(){
}

4. Verify OTP In your AccountController you must add the api end-point to verify the 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
        }
    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!