web-dev-qa-db-ja.com

ASP.NETIDを使用してWebAPI 2で2要素認証を実装するにはどうすればよいですか?

Web APIで2要素認証を作成する方法についてこのリンク goolgleオーセンティケーターを使用した2要素認証 を見ましたが、私の要件は少し異なります。

  1. アクセストークンの発行に2要素認証を使用したいと思います。 (ユーザーが2要素認証を有効にすることを選択した場合)
  2. ASP.NETID自体を使用してOTPコードを作成したいと思います。 (MVCWebアプリケーションで行う方法と同様にSignInManager.SendTwoFactorCodeAsync("Phone Code")

現在の実装の問題は、SignInManager.SendTwoFactorCodeAsync("Phone Code")を呼び出すと、ユーザーIDが見つからないというエラーが表示されることです。

デバッグするために、User.Identity.GetUserId();を呼び出してみましたが、正しいユーザーIDが返されます。

Microsoft.AspNet.Identity.OwinAssemblyのソースコードを確認しました

    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を呼び出し、2要素認証Cookieをチェックします。これはWebAPIプロジェクトであるため、Cookieが存在せず、0が返され、ユーザーIDが見つかりませんというエラーが発生します。

私の質問、asp.netIDを使用してWebAPIに2要素認証を正しく実装する方法は?

8
Anand Murali

これは、これを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メソッドは、次のようにパラメータを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, 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;
    }
}

。TwoFactorAuthorizeAttributeを使用

コントローラでTwoFactorAuthorizeAttributeを使用します

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

4。OTPの確認 AccountControllerで、OTPを確認するためのAPIエンドポイントを追加する必要があります

    [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
        }
    }
12
Spharah