Как реализовать двухфакторную аутентификацию в Web API 2 с помощью ASP.NET личность?

Я видел эту ссылку два фактора Auth с помощью Goolgle authenticator о том, как создать двухфакторную аутентификацию в веб-api, но мои требования немного отличаются.

  1. Я хочу использовать двухфакторную аутентификацию для выдачи маркера доступа. (Если пользователь решил включить двухфакторную аутентификацию)
  2. Я хотел бы создать код OTP, используя ASP.NET само тождество. (Как и в веб-приложении MVC SignInManager.SendTwoFactorCodeAsync("Phone Code")

проблема с моей текущей реализацией заключается в том, когда я вызываю SignInManager.SendTwoFactorCodeAsync("Phone Code") Я получаю ошибка пользователь не найден.

для отладки я попытался вызвать User.Identity.GetUserId(); и он возвращает правильный идентификатор пользователя.

Я проверил исходный код Microsoft.сеть САШ.Тождественность.Сборка долг

    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, в результате чего идентификатор пользователя не найден.

мой вопрос, Как правильно реализовать двухфакторную аутентификацию в веб-api, используя asp.net личность?

1 ответов


это то, что я реализовал, чтобы заставить это работать над api. я предполагаю, что вы используете значение по умолчанию ASP.NET шаблон одного пользователя.

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 метод замените paramenter на 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 enable, если он включен, он будет генерировать код проверки и отправлять его с помощью 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, 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. Используйте 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(), "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
        }
    }