4

我已經看到這個鏈接Two Factor Auth using goolgle authenticator關於如何在web api中創建一個雙因素身份驗證,但我的要求有點不同。如何在使用ASP.NET身份的Web API 2中實現雙因素驗證?

  1. 我想使用雙因素身份驗證發出訪問令牌。 (如果用戶選擇啓用雙因素身份驗證)
  2. 我想使用ASP.NET身份本身創建OTP代碼。 (喜歡的方式,我們做的MVC Web應用程序SignInManager.SendTwoFactorCodeAsync("Phone Code")

我當前實現,當我打電話SignInManager.SendTwoFactorCodeAsync("Phone Code")我得到錯誤的用戶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方法內部調用GetVerifiedUserIdAsync它檢查雙因素身份驗證cookie。由於這是一個web api項目,cookie不存在,返回0,導致用戶id找不到錯誤。

我的問題,如何在web api中使用asp.net標識正確實現雙因素身份驗證?

回答

5

這就是我已經實現了這個工作的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更換paramenter這樣的:

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; 
    } 
} 

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 
     } 
    } 
+0

Spharah嗨,非常感謝您的詳細解答。您是否也可以包含用於驗證用戶輸入的OTP代碼的邏輯? –

+0

嗨阿南德,我更新了答案,包括代碼來驗證OTP,不要忘記給予好評的答案:-) – Spharah

+0

Spharah,我已經投了。在接受答案之前,你可以通過疑問來澄清。成功的OTP驗證後,您將IsTwoFactorEnabled設置爲false。假設用戶在另一臺機器上再次登錄,現在他不會得到OTP(正確?),因爲IsTwoFactorEnabled設置爲false。你什麼時候重新啓用它? –

相關問題