How to correct issue with user being redirected to login after successful, but not to user page?

906 views Asked by At

In MVC4, I created a custom membership provider that returns true if the user authentication passes. No biggie here - this portion works the way it should:

    public override bool ValidateUser(string username, string password)
    {
        var crypto = new SimpleCrypto.PBKDF2(); // type of encryption
        // TODO: using (var unitOfWork = new Website.Repository.UnitOfWork(_dbContext))
        //var unitOfWork1 = new Website.Repository.UnitOfWork(_dbContext);

        using (var db = new Website.DAL.WebsiteDbContext())
        {
            var user = db.Users
                .Include("MembershipType")
                .FirstOrDefault(u => u.UserName == username);
            if (user != null && user.Password == crypto.Compute(password, user.PasswordSalt))
            {
                FormsAuthentication.SetAuthCookie(username, true);
                return true;
            }
        }
        return false;
    }

In my Login Action:

    [HttpPost]
    [AllowAnonymous]
    public ActionResult Login(Models.UserModel user)
    {
        if (ModelState.IsValid)
        {
            // custom membership provider
            if (Membership.ValidateUser(user.UserName, user.Password))
            {
                // Cannot use this block as user needs to login twice
                //if (User.IsInRole("WaitConfirmation"))  // checks the custom role provider and caches based on web.config settings
                //{
                //    //TempData["EmailAddress"] = thisUser.Email;

                //    // email address has not yet been confirmed
                //    return RedirectToAction("WaitConfirmation");
                //    //return View("Account", thisUser)
                //}
                //else
                //{
                //    // get custom identity - user properties
                //    string userName = UserContext.Identity.Name;
                //    //CustomIdentity identity = (CustomIdentity)User.Identity;
                //    var identity = UserContext.Identity;
                //    int userId = identity.UserId;

                //    return RedirectToAction("Index", "Dashboard");
                //}

                if (User.Identity.IsAuthenticated && User.IsInRole("WaitConfirmation"))  // checks the custom role provider and caches based on web.config settings
                {
                    return RedirectToAction("WaitConfirmation");
                }
                else if (User.Identity.IsAuthenticated)
                {
                    // get custom identity - user properties
                    string userName = UserContext.Identity.Name;

                    return RedirectToAction("Index", "Dashboard");
                }
            }
            else
            {
                ModelState.AddModelError("", "Login data is incorrect.");
            }
        }

        return View(user);
    }

In stepping through the code, when a user first logs in, User.Identity.IsAuthenticated is false and the page is redirected back to the login page. At this point if I either:

  • manually navigate to the user page (Dashboard) the user is details are available
  • login again, this works

I believe the answer lies somewhere in why the User.Identity.IsAuthenticated is not immediately true but can't figure out this is false the first time around.

The first block of commented-out code fails with Unable to cast object of type 'System.Security.Principal.GenericIdentity' to type 'Website.AdminWebsite.Infrastructure.CustomIdentity' as there is no IsAuthenticated check.

Suggestions?

2

There are 2 answers

1
mcsilvio On

This post describes a problem with similar symptoms.

http://forums.asp.net/t/1177741.aspx

Please have a read and ensure the order of your events (i.e. Authenticate, LoggedIn)

1
ElHaix On

After reading the article @mcsilvio's suggested, I added an RedirectToAction() as follows to initiate a new page life-cycle:

    public ActionResult Login(Models.UserModel user)
    {
        if (ModelState.IsValid)
        {
            // custom membership provider
            if (Membership.ValidateUser(user.UserName, user.Password))
            {
                return RedirectToAction("VerifyIdentity", user);
            }
            else
            {
                ModelState.AddModelError("", "Login data is incorrect.");
            }
        }

        return View(user);
    }

    public ActionResult VerifyIdentity(Models.UserModel user)
    {
        if (User.Identity.IsAuthenticated && User.IsInRole("WaitConfirmation"))  // checks the custom role provider and caches based on web.config settings
        {
            return RedirectToAction("WaitConfirmation");
        }
        else if (User.Identity.IsAuthenticated)
        {
            // get custom identity - user properties
            string userName = UserContext.Identity.Name;

            return RedirectToAction("Index", "Dashboard");
        }

        return View(User);
    }

This did the trick, but I'm wondering if there is a better way or is it always done like this?