Reputation: 1386
I'm looking into implementing two factor authentication in MVC, similar to Googles authenticator.
Since some users won't have two factor authentication setup, we want to use a two step process - one screen to enter the username and password, the other screen to enter the one time password.
My difficulty is how do you securely store the users username and password whilst they are entering their one time password? Currently we receive the password and immediately reject or issue a cookie, so we don't store the password anywhere. However, with two step we can't issue a cookie immediately because the user could then simply navigate to another action. Equally, I don't want to send the password back to the user as a hidden element in a form.
What is the standard practice for this situation?
The best I can think of is to store the username and password in the session, but I'm not sure how secure that is.
Upvotes: 10
Views: 17320
Reputation: 1314
While an answer has already been accepted I thought I would add a different way. You don't need to log the user in when you validate their username and password combination, if they have provided the correct details all you need to store in the temporary data is their username or their profile if you want, then redirecting them to the second factor page, which only once they have provided the correct one time password do you actually log the user in.
This method avoids the need for having additional attributes, which can be a pain for consistency.
This is the relevant snippet on how to achieve it
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
var profile = MvcTFAProfile.GetProfile(model.UserName);
if (profile.UsesTwoFactorAuthentication)
{
TempData[CurrentUserTempDataKey] = profile;
TempData[RememberMeTempDataKey] = model.RememberMe;
return RedirectToAction("SecondFactor", new {returnUrl = returnUrl});
}
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
return RedirectToLocal(returnUrl);
}
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
The following link contains all the details on how to implement this in ASP.NET MVC, the article targets Google Authenticator, which may not be what you're working with but the principle of how to log the user in etc. is the same; https://samjenkins.com/mvc-two-factor-authentication/
Upvotes: 9
Reputation: 4144
You should look at ASP.NET Identity for a sample flow for two-factor authentication. Following post has more info and links to the sample http://blogs.msdn.com/b/webdev/archive/2014/02/11/announcing-preview-of-microsoft-aspnet-identity-2-0-0-beta1.aspx
Upvotes: 1
Reputation: 658
Actually, you don't need to store the password and wait with your authentication until the second step is passed. You can just implement two steps of your authentication separately (each step is as usual authentication: you immediately authenticate or reject), and grant the appropriate authorities to users who passed the first step and the second step accordingly.
Specifically, you can create your own Authorize attribute AuthorizeConfirmedAttribute
derived from AuthorizeAttribute
and use it for your second step of authentication. So, in the controller where you generate your screen to enter the one time password you use the usual [Authorize]
attribute, ensuring that the user passed the first step of authentication. In all other actions you use the [AuthorizeConfirmed]
attribute to ensure that the user passed both steps of your authentication.
Upvotes: 5