Implementing "Remember Me" Feature in ASP.NET MVC
Asked Answered
S

1

27

I'm trying to implement a "remember me" feature to my login form. I am using ASP.NET MVC as my web application. I managed to get the cookie stuff working, but I failed to automatically login the user in case he/she checked the remember me checkbox before. I know what the problem is but I do not know how to solve it.

In my HomeController I have the following:

private LoginViewModel CheckLoginCookie()
{
    if (!string.IsNullOrEmpty(_appCookies.Email) && !string.IsNullOrEmpty(_appCookies.Password))
    {
        var login = new LoginViewModel
                        {
                            Email = _appCookies.Email,
                            Password = _appCookies.Password
                        };

        return login;
    }
    return null;
}


public ActionResult Index()
{
    var login = CheckLoginCookie();
    if (login != null)
        return RedirectToAction("Login", "User", login);

    var viewModel = new HomeIndexViewModel
                        {
                            IntroText =
                                "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.",
                            LastMinuteDeals = new List<ItemsIndexViewModel>(),
                            TrustedDeals = new List<ItemsIndexViewModel>()
                        };
    return View(viewModel);
}

And in my UserController, I have the Login action method:

public ActionResult Login()
{
    return PartialView(new LoginViewModel());
}

[HttpPost]
public ActionResult Login(LoginViewModel dto)
{
    bool flag = false;
    if (ModelState.IsValid)
    {
        if (_userService.AuthenticateUser(dto.Email, dto.Password, false)) {
            var user = _userService.GetUserByEmail(dto.Email);
            var uSession = new UserSession
            {
                ID = user.Id,
                Nickname = user.Nickname
            };
            SessionManager.RegisterSession(SessionKeys.User, uSession);
            flag = true;

            if(dto.RememberMe)
            {
                _appCookies.Email = dto.Email;
                _appCookies.Password = dto.Password;
            }
        }
    }
    if (flag)
        return RedirectToAction("Index", "Home");
    else
    {
        ViewData.Add("InvalidLogin", "The login info you provided were incorrect.");
        return View(dto);
    }
}

So basically, what I thought I would do is to redirect the user from the Index action result on the home controller in case there was a login cookie. But the problem is that the RedirectToAction will trigger the GET Login action method and not the POST which takes care of logging in the user.

Am I going completely wrong about this? Or is there some way I could call the POST Login method using RedirectToAction or any other way?

Sprat answered 11/4, 2011 at 10:11 Comment(0)
N
61

First off, you should never store the user's credentials in a cookie. It's incredibly insecure. The password will be passed with every request as well as being stored in plain text on the user's machine.

Second, don't reinvent the wheel, especially when security is concerned, you'll never get it right.

ASP.Net already provides this functionality securely with Forms Authenitcation and Membership Providers. You should take a look into that. Creating a default MVC project will include the basic authentication setup. The official MVC site has more.

Update

You can still use .NET forms authentication without implementing a membership provider. At a basic level it would work like this.

You enable forms authentication in you web.config

<authentication mode="Forms">
  <forms loginUrl="~/Account/Login" timeout="2880" />
</authentication>

You decorate the actions or the controllers you would like to secure with the [Authorize] attribute.

[Authorize]
public ViewResult Index() {
  //you action logic here
}

Then create a basic login action

[HttpPost]
public ActionResult Login(LoginViewModel dto) {

  //you authorisation logic here
  if (userAutherised) {
    //create the authentication ticket
    var authTicket = new FormsAuthenticationTicket(
      1,
      userId,  //user id
      DateTime.Now,
      DateTime.Now.AddMinutes(20),  // expiry
      rememberMe,  //true to remember
      "", //roles 
      "/"
    );

    //encrypt the ticket and add it to a cookie
    HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName,   FormsAuthentication.Encrypt(authTicket));
    Response.Cookies.Add(cookie);

    return RedirectToAction("Index");

  }

}
Nadya answered 11/4, 2011 at 10:34 Comment(12)
+1 especially for create a default mvc project. If I have any project that needs login functionality, I create a new mvc internet app, and then change the code from there.Arlettaarlette
@David Glenn: I totally understand but I already went through this when I first started working on this project and decided not to use the ASP.NET Membership API because it's bulky, ugly and does not fit my needs. Plus I'm using EF Code First as my ORM so I could not find a way to work with the Membership API and generate the database with code. Anyway, if I'm not going to store credentials in a cookie, how do I implement the Remember Me feature?Sprat
Store their ID in the cookie. Then lookup their user in the repository upon login to get the credentials.Liver
You can still use the basic .NET forms authentication without implementing a membership provider. .NET will handle the 'remember me' functionality for you. It creates a secure encrypted cookie that contains an authentication ticket. The authentication ticket contains user ID and lifespan of the ticket. It then automatically checks the ticket when using the AuthorizeAttributeNadya
@Rory McCrossan:please don't store the user ID in a cookie. It takes a few clicks for a user to edit that cookie and have himself logged in as another user.Syringomyelia
@David Glenn: This looks awesome! I did not know I could use forms authentication without the Membership API! Thanks for the tip. One more thing though, does that code snippet solve the initial problem of this thread? If I understood correctly, decorating the Index action method of the home controller with the Authorize attribute should do the trick?Sprat
You should place the AuthorizeAttribute on any controller or action that you want to appear behind the login, not just the homepage.Nadya
@David Glenn: Hmmm... I'll give it a try and let's see if it gets the job done. Thanks again, much appreciated :)Sprat
@David Glenn: So this did not really solve the main issue of this thread though it helped me improve my authentication system. David, any suggestions on how to have it log users automatically after they've checked the remember me checkbox?Sprat
If you set the lifetime of the authentication ticket and the expiry date of the cookie to a future date then the user will be logged in until the designated date or they logout. That's how the user is remembered.Nadya
I had to add this for it to work correctly: if (authTicket.IsPersistent) { cookie.Expires = authTicket.Expiration; } Just before adding the cookie to the responseMangonel
Can , somebody tell me how to check cookies when open the site again ?Undue

© 2022 - 2024 — McMap. All rights reserved.