home | articles | site map | contacts
about us
consulting
client login
support
contacts
MVC Forms Authentication and Storing Data in the Cookie
12/7/2012
Follow PrimaryObjects on Twitter Subscribe to Primary Objects via RSS More Software Articles
by Primary Objects
enter email address
 

Introduction

Forms authentication is a common feature in many C# MVC .NET web applications. There are a variety of methods for implementing forms authentication in MVC .NET. A key part for each, is the process of storing user specific details that are carried throughout the web application. User details such as Id, Username, Address, and Age may be obtained through various methods, such as by querying the database upon every request or as needed, loading from cache, context, or even loading from Session.

In this tutorial, we'll walk through the steps of implementing forms authentication in C# MVC .NET, specifically with MVC4. We'll use a custom MembershipProvider class, along with a custom Principal object. The Principal will hold our custom user details, encrypted within the forms authentication ticket cookie, and allow us to access this data anywhere within the web application.

Setting up the Web.Config for Forms Authentication

The first step for implementing MVC4 .NET forms authentication is to configure the web.config with the associated tags. We'll need a configuration for the authentication type and another for the membership provider, as shown below.

<authentication mode="Forms">
  <forms loginurl="~/" slidingexpiration="true" timeout="20"></forms>
</authentication>
    
<membership defaultprovider="MyMembershipProvider">
  <providers>
    <clear>
    <add name="MyMembershipProvider" type="MVC4FormsAuth.Web.Providers.MyMembershipProvider">
  </add></clear></providers>
</membership>

In the above configuration, we're setting the forms authentication tag and providing the membership provider details. We've included the name of our custom MembershipProvider, as well as the type property, which combines the assembly and class name. The membership provider class is an important part of forms authentication, as it allows us to easily authenticate a user that is logging in. It also assists us in configuring the HttpContext.User.Identity property.

Logon Types

Before beginning the code setup for C# MVC .NET forms authentication, we'll need some basic types to handle maintaining the user details after logging in.


public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Username { get; set; }
    public int Age { get; set; }
}

public class MyPrincipal : IPrincipal
{
    public MyPrincipal(IIdentity identity)
    {
        Identity = identity;
    }

    public IIdentity Identity
    {
        get;
        private set;
    }

    public User User { get; set; }

    public bool IsInRole(string role)
    {
        return true;
    }
}

The User class will hold details read from the database. We'll store a copy of the User class within the forms authentication ticket, so it's important to omit sensitive information from this class. Any sensitive or frequently-changing data should be read on-demand, when an action is initiated.

The MyPrincipal class is a custom Principal object that will be used with the HttpContext.User.Identity to allow us to load the forms authentication custom user data. We'll be serializing the User class as a JSON string and storing it in the forms authentication ticket.

Logging In

We can create our initial controller method for the Login call with a basic MVC4 .NET controller method, as shown below:


[HttpPost]
public JsonResult Login(Logon logon)
{
    string status = "The username or password provided is incorrect.";

    // Verify the fields.
    if (ModelState.IsValid)
    {
        // Authenticate the user.
        if (UserManager.ValidateUser(logon, Response))
        {
            // Redirect to the secure area.
            if (string.IsNullOrWhiteSpace(logon.RedirectUrl))
            {
                logon.RedirectUrl = "/";
            }

            status = "OK";
        }
    }

    return Json(new { RedirectUrl = logon.RedirectUrl, Status = status });
}

In the above controller method, we authenticate the user with our custom membership provider and create the forms authentication ticket. The above method is using an ajax form submission, and therefore, returns a JsonResult string, rather than an actual MVC .NET view.

Validating the User

The login controller calls our ValidateUser helper method, which in turn, calls the custom membership provider to authenticate the user. The .NET membership provider framework will call our custom MyMembershipProvider class and execute our AuthenticateUser() method.

If the membership provider call succeeds, we can create the forms authentication cookie. The membership provider will store the resulting User object within HttpContext.Current.Items. This collection is valid only for the current request, which is just enough time for us to grab it and save it in the cookie. It's important to note that Session is not yet available in this call.

We'll first serialize the returned User object as a JSON string. We'll then create the forms authentication ticket and provide the JSON string as our custom user data. The entire ticket will be encrypted and stored as the C# MVC4 .NET forms authentication cookie, which we can later retrieve and decrypt to access its contents.


public static bool ValidateUser(Logon logon, HttpResponseBase response)
{
    bool result = false;

    if (Membership.ValidateUser(logon.Username, logon.Password))
    {
        // Create the authentication ticket with custom user data.
        var serializer = new JavaScriptSerializer();
        string userData = serializer.Serialize(UserManager.User);

        FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1,
                logon.Username,
                DateTime.Now,
                DateTime.Now.AddDays(30),
                true,
                userData,
                FormsAuthentication.FormsCookiePath);

        // Encrypt the ticket.
        string encTicket = FormsAuthentication.Encrypt(ticket);

        // Create the cookie.
        response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket));

        result = true;
    }

    return result;
}

The Custom Membership Provider

As part of the login process, our Controller method has called ValidateUser(), which in turn, calls the custom membership provider's ValidateUser() method. We can define the custom membership provider by inheriting from the MembershipProvider base class. Since we'll only need two of the methods, defined by the base class, we can implement our custom MembershipProvider in C# MVC4 .NET as follows:


public class MyMembershipProvider : MembershipProvider
{
    public override bool ValidateUser(string username, string password)
    {
        // Check if this is a valid user.
        User user = UserManager.AuthenticateUser(username, password);
        if (user != null)
        {
            // Store the user temporarily in the context for this request.
            HttpContext.Current.Items.Add("User", user);

            return true;
        }
        else
        {
            return false;
        }
    }

    public override MembershipUser GetUser(string username, bool userIsOnline)
    {
        if (UserManager.User != null)
        {
            return new MembershipUser("MyMembershipProvider", username,
                       UserManager.User.Id, UserManager.User.Username, null,
                       null, true, false, DateTime.MinValue, DateTime.MinValue,
                       DateTime.MinValue, DateTime.MinValue, DateTime.MinValue);
        }
        else
        {
            return null;
        }
    }
}

In the above code, we've provided the methods for ValidateUser() and GetUser(). Our ValidateUser method first calls our own user authentication method to check the user's username and password. If the check succeeds, we are returned a User object. We'll need to store this User object in the forms authentication ticket. However, since Session will not be available in the Application_AuthenticateRequest() method, we'll need a way to temporarily store the User object for the current request. We can take advantage of HttpContext.Current.Items for this, as this object holds values for the duration of the request. This will give us enough time to read the User and store it in the forms authentication ticket custom data.

The helper method for actually authenticating the user (ie., calling the database or web service), is shown below:


public static User AuthenticateUser(string username, string password)
{
    User user = null;

    // Lookup user in database, web service, etc. We'll just generate a fake user for this demo.
    if (username == "john" && password == "doe")
    {
        user = new User { Id = 123, Name = "John Doe", Username = "johndoe", Age = 21 };
    }

    return user;
}

The AuthenticateUser method would typically call the database or web server to verify the user details. In this example, we simply check against a hard-coded account and return a sample User. The series of calls is finally returned back to the ValidateUser() helper method, where we can continue by encrypting the forms authentication ticket and saving the cookie. The user is now successfully logged in.

Getting the User Data

Once the user is logged in, we'll often need to retrieve his data. For example, we'll probably need to show his name, address, and other information. Some of this data will probably be regularly used throughout the C# MVC4 web application. Since we've stored this data in the forms authentication ticket, we've greatly enhanced our web application by not having to query the database or web service to retrieve these details. Instead, we can query the HttpContext.Current.Items of the current request, which contains our decrypted User object from the forms authentication ticket.

Authenticating the Current Request

To begin, we need to implement the Application_AuthenticateRequest() in the Global.asax.cs file. This method is called on each request in the web application, so you'll want to keep it lightweight, in nature. While this example includes the below code for every request, you could also create a custom MVC4 filter to obtain the data only on specific web application actions. Our AuthenticateRequest call appears as follows:


protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
    HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
    if (authCookie != null)
    {
        // Get the forms authentication ticket.
        FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
        var identity = new GenericIdentity(authTicket.Name, "Forms");
        var principal = new MyPrincipal(identity);

        // Get the custom user data encrypted in the ticket.
        string userData = ((FormsIdentity)(Context.User.Identity)).Ticket.UserData;

        // Deserialize the json data and set it on the custom principal.
        var serializer = new JavaScriptSerializer();
        principal.User = (User)serializer.Deserialize(userData, typeof(User));

        // Set the context user.
        Context.User = principal;
    }
}

In the above code, we first check if a forms authentication ticket cookie exists for the currently connected user. If the cookie exists, we read it and attempt to decrypt it. With the decrypted cookie, we create the custom Principal object and provide it with the decrypted user details from the cookie. This will allow us to access the user details from the standard HttpContext.Current.User property in the .NET MVC web application.

Accessing the User Data in the Ticket

Since our AuthenticateRequest method has created the User.Identity and included our custom User data within it, we simply need to cast the Context.User object to our custom User type. We'll then be able to easily access the user details from anywhere within our MVC .NET web application. We can create a helper method for making this more convenient, as shown below:


public static User User
{
    get
    {
        if (HttpContext.Current.User.Identity.IsAuthenticated)
        {
            // The user is authenticated. Return the user from the forms auth ticket.
            return ((MyPrincipal)(HttpContext.Current.User)).User;
        }
        else if (HttpContext.Current.Items.Contains("User"))
        {
            // The user is not authenticated, but has successfully logged in.
            return (User)HttpContext.Current.Items["User"];
        }
        else
        {
            return null;
        }
    }
}

We'll use the above helper method for logging the user in (as shown above in the ValidateUser helper method), as well as accessing the user's details after he's already logged in.

The above User helper method first checks if the user is already authenticated. If he is, we simply cast the HttpContext.Current.User object to our custom principal object, MyPrincipal, and then return the User property. Remember, this object was created in the AuthenticateRequest() method, as part of the MVC ASP .NET framework.

If the user is not yet logged in, we'll check the HttpContext.Current.Items for the User object. This is stored as part of the custom membership provider (since we don't have access to Session at the time of logging in, we use the HttpContext to store the data temporarily for the current request).

Displaying the User Data

Finally, our framework is complete. The user has been authenticated, logged in, and is now available in our application by accessing the HttpContext.Current.User or our helper User class. We can access the user in the MVC Razor view, as shown below:


@if (User.Identity.IsAuthenticated)
{
   You're logged in as @UserManager.User.Username (@UserManager.User.Age)
}
else
{
   @Html.Partial("Controls/Login", new Logon());
}
    

The above MVC Razor view checks if the user is authenticated. If he is, we access the User helper object (obtained via the forms authentication cookie and associated encrypted data). If the user is not yet logged in, we show the login form.

Logging Out

When the user is ready to log out, we'll simply need to abandon the session, sign out of the forms authentication, and clear any remaining forms authentication cookie by allowing it to expire. We can do this with a helper method, as shown below:


public static void Logoff(HttpSessionStateBase session, HttpResponseBase response)
{
    // Delete the user details from cache.
    session.Abandon();

    // Delete the authentication ticket and sign out.
    FormsAuthentication.SignOut();

    // Clear authentication cookie.
    HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, "");
    cookie.Expires = DateTime.Now.AddYears(-1);
    response.Cookies.Add(cookie);
}

The above method may be called from your Logoff controller method.

Download @ GitHub

You can download the project source code on GitHub by visiting the MVC4 Forms Authentication example project.

About the Author

This article was written by , Microsoft certified software developer and architect, providing C# ASP .NET Javascript web application development, database design, and mobile software development across a variety of domains for clients in both the business and consumer sectors.


   
comments powered by Disqus
Profile
Learn more about Primary Objects and our goals ..  More
12/28/2012
Primary Objects publishes node.js app, RedAnt, a REST web service .. More
09/21/2012
Primary Objects publishes node.js app, CD Early Withdrawal Calculator .. More
Home | About Us | Services | Client Login | Job Opportunities | Contact Us
Copyright © Primary Objects 2013
Privacy Policy
Follow us on Twitter