Two-step authentication in ASP.Net MVC

I am working on an ASP.NET Mvc 3 application using FormsAuthentication with a custom MembershipProvider (so I have some control over what the provider returns).

Requirements require a two-step authentication process (username and password followed by a security question). The user should not have access to any of the "safe" sections of the site without going through both steps. Please do not mention if this is multi-factor security or not, I already know.

Please provide guidance on how best to accomplish this task.

Here are some considerations:

  • I am allowed (architecturally) to use the session - I would rather not do this.
  • I would prefer to use the ActionFilter [Authorize] outside the box for controllers that provide secure content.
  • Responsible people would like the URL for the two steps to be the same: i.e. www.contoso.com/login/ . At least in my attempts, this caused some minor, but not insignificant problems, when users entered the wrong answer in the second stage (they were not officially logged in, but I need to make sure that I still work against the semi-authenticated user question / answer).

Thanks.

+6
source share
2 answers

Use a custom view model along with hidden form fields. Just make sure everything is done over https.

ViewModel

 public LoginForm { public string UserName { get; set; } public string Password { get; set; } public int SecretQuestionId { get; set; } public string SecretQuestion { get; set; } public string SecretQuestionAnswer { get; set; } } 

Action methods

 public ActionResult Login() { var form = new LoginForm(); return View(form); } [HttpPost] public ActionResult Login(LoginForm form) { if (form.SecretQuestionId == 0) { //This means that they've posted the first half - Username and Password var user = AccountRepository.GetUser(form.UserName, form.Password); if (user != null) { //Get a new secret question var secretQuestion = AccountRepository.GetRandomSecretQuestion(user.Id); form.SecretQuestionId = secretQuestion.Id; form.SecretQuestion = secretQuestion.QuestionText; } } else { //This means that they've posted from the second half - Secret Question //Re-authenticate with the hidden field values var user = AccountRepository.GetUser(form.UserName, form.Password); if (user != null) { if (AccountService.CheckSecretQuestion(form.SecretQuestionId, form.SecretQuestionAnswer)) { //This means they should be authenticated and logged in //Do a redirect here (after logging them in) } } } return View(form); } 

View

 <form> @if (Model.SecretQuestionId == 0) { //Display input for @Model.UserName //Display input for @Model.Password } else { //Display hidden input for @Model.UserName //Display hidden input for @Model.Password //Display hidden input for @Model.SecretQuestionId //Display @Model.SecretQuestion as text //Display input for @Model.SecretQuestionAnswer } </form> 

If you are unsatisfied with sending the username and password back to the view in hidden fields for re-authentication and make sure they are not cheating ... you can create an HMAC or something similar for testing.

Btw, this question seems to be several questions nested into one ... so it just answered how to do two-step authentication using a single view / action method.

+4
source

I would probably do something where the first step forces them to enter a username and password. Check it out, if it’s good, move them to the flag-enabled view that asks them to answer the question. If they do not succeed, write them out, download them, whatever. I do not think that this is possible in one view, if you do not make a partial view, and if they leave without completing the authentication process, you write them out and clear their cookie.

---- EDIT ----- On the other hand, you can make a partial view, just do not write them down until they complete the second part of the partial view. Some psuedo code:

 public ActionResult Login(){ get username and password off the view if its valid render a partial view that asks for the secret answer if thats valid forms auth login else try again, get booted, or whatever else get booted, try again, whatever } 
0
source

Source: https://habr.com/ru/post/888948/


All Articles