Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC4 how do I set a cookie and then redirect to an action

Hi I am trying to get a users role and set it to a cookie in my application

I have the following code which works

public ActionResult Index()
    {
        var user = User.Identity.Name;  // set by 3rd party central login in manager

        // key to check that we are in our environment with 3rd party login set up
        if (ConfigurationManager.AppSettings["IsNGDC"] == "true")
        {
            // ActiveKey login
            if (user.Contains("uid="))
            {
                var endIndex = user.IndexOf(",ou");

                var userEmail = user.Substring(4, endIndex - 4);
                user = userEmail;
            }

            SetAuthenticationCookie(user);
        }

        // view model is not needed I could just pass in a string
        var viewModel = new SiteminderViewModel { Username = user };

        if (ModelState.IsValid)
        {
            this.AssignRoles(viewModel);
            return this.View();
        }

        return View(viewModel);
    }

I need to change this because I am using a dynamic Navigation bar that shows different Items depending on the users role and it does not show the correct Nav bar until the user refreshes the page. I think this is because the view uses the cookie and the view is being rendered in the same action that sets the cookie.

I want to split this into 2 actions in my controller as follows

    public void LogIn()
    {
        var user = User.Identity.Name;  // set by 3rd party central login in manager

        // key to check that we are in our environment with 3rd party login set up
        if (ConfigurationManager.AppSettings["IsNGDC"] == "true") 
        {
            // ActiveKey login
            if (user.Contains("uid="))
            {
                var endIndex = user.IndexOf(",ou");

                var userEmail = user.Substring(4, endIndex - 4);
                user = userEmail;
            }

            SetAuthenticationCookie(user);
        }

        // view model is not needed I could just pass in a string
        var viewModel = new SiteminderViewModel { Username = user };

        this.AssignRoles(viewModel);

        // default URL in Index action for this controller
        this.Response.Redirect(FormsAuthentication.DefaultUrl, false);
    }

    public ActionResult Index()
    {
        ViewBag.Message = "Home App Description here";
        return this.View();
    }

When I try this it looks like the Cookie hasn't been set. Unfortunately I can only test this code on a replication of our Production Environment because of the 3rd party login so I have limited debug info. As far as I can tell the problem seems to be with how I am redirecting.

I have provided the methods I use cor creating the cookie and assigning the roles bellow.

Additional Info

    private void SetAuthenticationCookie(string username)
    {
        var tkt = new FormsAuthenticationTicket(1, username, DateTime.UtcNow, DateTime.UtcNow.AddMinutes(20), true, string.Empty);
        var encryptedTkt = FormsAuthentication.Encrypt(tkt);

        var formsCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTkt);
        this.Response.Cookies.Add(formsCookie);
    }

    private void AssignRoles(SiteminderViewModel viewModel)
    {
        var authCookie = System.Web.HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
        var ticket = authCookie != null ? FormsAuthentication.Decrypt(authCookie.Value) : new FormsAuthenticationTicket(1, viewModel.Username, DateTime.UtcNow, DateTime.UtcNow.AddMinutes(20), true, string.Empty);
        var user = this.userRepository.GetUser(viewModel.Username);

        if (user != null)
        {
            var principleProperties = new PrincipleProperties(ticket.UserData)
            {
                UserName = user.Email,
                UserRole = user.UserGroup.Role.Name.Replace(" ", string.Empty),
                ContextId = contextRepository.GetContextByDataOwnerGroupId(user.UserGroupId)
            };

            if (user.DeletedIndicator)
            {
                principleProperties.UserRole = string.Empty;
            }

            this.SetPrinciple(ticket, principleProperties);
        }
    }

    private FormsAuthenticationTicket SetPrinciple(FormsAuthenticationTicket ticket, PrincipleProperties properties)
    {
        var newticket = new FormsAuthenticationTicket(
            ticket.Version,
            ticket.Name,
            ticket.IssueDate,
            ticket.Expiration,
            ticket.IsPersistent,
            properties.Serialize(),
            ticket.CookiePath);

        var encryptedTkt = FormsAuthentication.Encrypt(newticket);

        var formsCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTkt);
        this.Response.Cookies.Set(formsCookie);

        var referenceDataIdentity = new ReferenceDataIdentity(ticket);
        var principle = new ReferenceDataPrinciple(referenceDataIdentity, properties);

        Thread.CurrentPrincipal = principle;
        return newticket;
    }
like image 688
Jeff Finn Avatar asked Jan 13 '14 11:01

Jeff Finn


People also ask

How do I redirect to another action?

The RedirectToAction() Method This method is used to redirect to specified action instead of rendering the HTML. In this case, the browser receives the redirect notification and make a new request for the specified action.

How do I redirect a controller to another action?

Redirect() method The Rediect() method is available to your controller from the ControllerBase class. It accepts a target URL where you would like to go. For example, consider the following two actions of HomeController. The Index() action invokes the Redirect() method by specifying the URL of the Privacy() action.

How redirect to action in view in MVC?

return RedirectToAction() To redirect to a different action which can be in the same or different controller. It tells ASP.NET MVC to respond with a browser to a different action instead of rendering HTML as View() method does.


1 Answers

The solution to this was that the cookie wasn't being added to the browser because I was redirecting before the cookie reached the client side the solution was to have the Login Action return a blank view and then from inside the view redirect to the Index action the final version of my code ended up as follows NOTE: Login changed to AuthenticateUser

    public ActionResult Index()
    {
        var authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
        if (authCookie != null)
        {
            var ticket = FormsAuthentication.Decrypt(authCookie.Value);

            if (ticket != null && ticket.UserData != string.Empty)
            {
                return this.View();
            }
        }

        return RedirectToAction("AuthenticateUser");
    }

    public ActionResult AuthenticateUser()
    {
        // set by Site minder
        var user = User.Identity.Name;

        // ActiveKey login
        if (user.Contains("uid="))
        {
            var endIndex = user.IndexOf(",ou");

            var userEmail = user.Substring(4, endIndex - 4);
            user = userEmail;
        }

        SetAuthenticationCookie(user);


        var viewModel = new SiteminderViewModel { Username = user };

        this.AssignRoles(viewModel);
        return this.View();
    }

and the view is. There is no HTML to display so the redirect is not noticeable.

@{
    ViewBag.Title = "AuthenticateUser";
    Layout = null;
    Response.Redirect( Url.Action("Index", "Home"), false);
}

This code is checking that there is a cookie and that the user data isn't empty, if theses checks pass it shows the user the home page. Otherwise it redirects to the authentication action that gets the email address that was set in the browser by our 3rd party central login software and gets the users details from the users details. If the user is not in our user table they are given basic access rights.

like image 109
Jeff Finn Avatar answered Sep 21 '22 23:09

Jeff Finn