top button
Flag Notify
    Connect to us
      Site Registration

Site Registration

Implementing Ajax Login In ASP.NET MVC

+5 votes
453 views

Recently during a training program one of the participant asked this question - "How to create a login page using jQuery Ajax in MVC applications?" This article is illustrates how Ajax login can be implemented using Forms authentication, Membership and jQuery $.ajax().

Implementing Ajax based login involves many of the same steps as the normal forms authentication. However, the login page doesn't send user ID and password to the server through a standard form submission. Instead, user credentials are sent to the server via an Ajax request. The credentials are then validated on the server and the result of the verification process is conveyed to the client. If the login attempt was successful, the user is taken to the secured area of the website.

Let's understand how all this works by developing a sample application. Begin by creating a new ASP.NET MVC Web Application using an empty template. To keep things simple we will add only those things to the project that are absolutely essential to the functioning of this example.

Configure a database for membership services

First of all you need to configure a database for membership services. This is done with the help of aspnet_regsql.exe tool. Open Visual Studio developer command prompt and issue the said command to open the configuration wizard. Simply follow the wizard to configure your database. For example, here I am configuring Northwind database to support membership services.

image

Configure forms authentication and membership provider

Next, open the web.config of the web application and configure the authentication scheme as shown below:

<connectionStrings>
  <add name="connstr" connectionString="data source=.\sqlexpress;
                      initial catalog=Northwind;integrated security=true"/>
</connectionStrings>

<system.web>
  <authentication mode="Forms">
    <forms loginUrl="~/account/login" defaultUrl="~/home/index"></forms>
  </authentication>
  ...
</system.web>

The <authentication> tag sets the authentication mode to Forms. The forms authentication is configured to have login page as ~/account/login and default page as ~/home/index. The <connectionStrings> section defines a database connection string for the Northwind database. This connection string is used while configuring the membership provider.

To configure the membership provider add the following markup to the web.config file:

<membership defaultProvider="p1">
  <providers>
    <add name="p1" connectionStringName="connstr" 
                   type="System.Web.Security.SqlMembershipProvider" />
  </providers>
</membership>

Create Account controller

Then add a controller to the Controllers folder - AccountController. The Account controller contains code that validates a user. The Login() and ValidateUser() action methods of the Account controller are shown below:

public ActionResult Login()
{
    return View();
}

[HttpPost]
public JsonResult ValidateUser(string userid, string password, 
                               bool rememberme)
{
    LoginStatus status = new LoginStatus();
    if (Membership.ValidateUser(userid, password))
    {
        FormsAuthentication.SetAuthCookie(userid, rememberme);
        status.Success = true;
        status.TargetURL = FormsAuthentication.
                           GetRedirectUrl(userid, rememberme);
        if (string.IsNullOrEmpty(status.TargetURL))
        {
            status.TargetURL = FormsAuthentication.DefaultUrl;
        }
        status.Message = "Login attempt successful!";
    }
    else
    {
        status.Success = false;
        status.Message = "Invalid UserID or Password!";
        status.TargetURL = FormsAuthentication.LoginUrl;
    }
    return Json(status);
}

The Login() action method simply returns the Login view. The ValidateUser() method is important to us because this method validates the user credentials and is called via Ajax. The ValidateUser() method takes three parameters - userid, password and rememberme. Inside, it calls ValidateUser() method of the Membership object to decide whether the user ID and password is correct. The ValidateUser() method also creates an instance of LoginStatus class - a POCO that is intended to store the status of the login process. The LoginStatus class looks like this:

public class LoginStatus
{
    public bool Success { get; set; }
    public string Message { get; set; }
    public string TargetURL { get; set; }
}

The LoginStatus class consists of three properties - Success, Message and TargetURL. The Success boolean property holds true if the login attempt was successful, false otherwise. The Message property holds a succcess or error message that is to be displayed to the end user. The TargetURL property holds the page URL where the user should be redirected if the login attempt was successful.

Coming back to the ValidateUser() method, if the user credentials are valid a forms authentication cookie is issued using the SetAuthCookie() method. The LoginStatus object is populated with the required information. Notice how the TargetURL is determined using GetRedirectUrl() method and DefaultUrl properties of the FormsAuthentication class.

If the login attempt was unsuccessful, LoginStatus object is populated with error information. Finally, LoginStatus object is sent back to the caller using Json() method. Remember that ValidateUser() method will be called using Ajax and hence should return data to the browser in JSON format.

Create Login view

Now add the Login view and design it as shown below:

image

The Login view consists of a textbox, a password box, a checkbox and a button. Clicking on the Login button initiates an Ajax request to the ValidateUser() method you created earlier. The jQuery code responsible for calling the ValidateUser() method is given below:

$(document).ready(function () {
    $("#login").click(function () {
        $("#message").html("Logging in...");
        var data = { "userid": $("#userid").val(), 
                     "password": $("#password").val(), 
                     "rememberme":$("#rememberme").prop("checked") };
        $.ajax({
            url: "/account/validateuser",
            type: "POST",
            data: JSON.stringify(data),
            dataType: "json",
            contentType: "application/json",
            success: function (status) {
                $("#message").html(status.Message);
                if (status.Success)
                {
                    window.location.href = status.TargetURL;
                }
            },
            error: function () {
                $("#message").html("Error while authenticating 
                                    user credentials!");
            }
        });
    });
});

Observe this code carefully. Upon clicking the login button a progress message is displayed in a message <div> element. The code then forms a JavaScript object that has three properties - userid, password and rememberme. These property names must match the parameter names of the ValidateUser() action method you created earlier. Then $.ajax() of jQuery is used to make an Ajax request to /account/validateuser. The type of the request is set to POST. The data setting contains the stringified version of the data JavaScript object you just created. The dataType and contentType properties are set to json and application/json respectively. These two properties represent the response format and the request MIME content type respectively. The success function receives a status object. This object is a JSON representation of LoginStatus object you return from the ValidateUser() method. If the Success property is true it means that the login attempt was successful and the user is redirected to the TargetURL using window.location.href property. If the login attempt fails an error message is displayed in the message <div>. The error function is invoked if there is any error making the Ajax call and simply displays an error message to the user.

The following figure shows the login view in action:

image

Create Home controller and Index view

If a login attempt is successful the use is taken to the Index view. So, add the Home controller and also the Index view. The Home controller is supposed to be a secured one and hence add [Authorize] attribute on top of the Index() action method or the HomeController class.

[Authorize]
public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }
}

The Index view simply outputs a welcome message:

<body>
<h1>Welcome @Membership.GetUser().UserName!</h1>
</body>

The following figure shows a successful run of the Index view:

image

 

To test the application you just developed you need to create a new user account. You can do so either by creating a registration page or by adding a few test users in the Global.asax. For the sake of this example the later approach is alright. Here is how you can create a new user.

protected void Application_Start()
{
  ...
  MembershipCreateStatus status;
  Membership.CreateUser("User1", 
  "some_password_here", "user1@somewebsite.com", 
  "question", "answer", true, out status);
}

That's it! The Ajax login for your MVC application is ready :-)

posted Oct 20, 2016 by Shivaranjini

  Promote This Article
Facebook Share Button Twitter Share Button LinkedIn Share Button


Related Articles

In data entry forms involving textboxes with predictable values one can use autocomplete to assist user pick an existing value. HTML5 introduces <datalist> element that can come handy while implementing autocomplete. The <datalist> element holds a list of options and can be attached with a textbox using list attribute. By adding a bit of jQuery Ajax you can dynamically populate the options in a <datalist>. This article shows you how to do just that.

HTML5 <datalist> element is used as shown in the following markup.

<input type="text" list="datalist1" />

<datalist id="datalist1">
  <option value="US" label="United States" />
  <option value="UK" label="United Kingdom" />
  <option value="IN" label="India" />
</datalist>

At runtime the above markup shows an autocomplete as shown below:

image

Once you select an option from the list, the textbox is filled with the value. The <datalist> and its <option> elements are statically placed in the above markup. If you wish to dynamically populate the <datalist> based on the value entered in the textbox, you need to make an Ajax call to the server and fetch the required data.

Consider the following HTML markup that has a textbox and an empty <datalist>.

<h1>Autocomplete Example</h1>
<input id="companyName" list="companyList" />
<datalist id="companyList"></datalist>

To dynamically populate the <datalist> you can add the following jQuery code:

$(document).ready(function () {
    $("#companyName").on("input", function () {
        var options = {};
        options.url = "/home/getcompanylist";
        options.type = "GET";
        options.data = { "criteria": $("#companyName").val() };
        options.dataType = "json";
        options.success = function (data) {
            $("#companyList").empty();
            for(var i=0;i<data.length;i++)
            {
                $("#companyList").append("<option value='" + 
                data[i].CompanyName + "'></option>");
            }
        };
        $.ajax(options);
    });

});

The above jQuery code wires the input event handler for the companyName textbox. The code makes an Ajax call to an MVC action method - GetCompanyList(). This action method returns a list of CompanyName values from Customers table of the Northwind database. While making the Ajax call you pass the textbox value to the action method as the search criteria. The success function receives an array of JavaScript objects. Each object has a single property - CompanyName - that is then filled in <option> elements of companyList.

The GetCompanyList() action method finds all the company names containing the entered text. The GetCompanyList() action is shown below:

public JsonResult GetCompanyList(string criteria)
{
    NorthwindEntities db = new NorthwindEntities();
    var query = (from c in db.Customers
                    where c.CompanyName.Contains(criteria)
                    orderby c.CompanyName ascending
                    select new { c.CompanyName }).Distinct();
    return Json(query.ToList(),JsonRequestBehavior.AllowGet);
}

Notice that GetCompanyList() action returns JsonResult using Json() method. Also notice that JsonRequestBehavior is set to AllowGet so that GET requests can call this method.

That's it! You can now run the application and test whether it dynamically displays the autocomplete. The following figure shows a sample run.

image

READ MORE

Sometimes you need to select records for certain action using checkboxes. For example, you may select records for deleting and then delete them from the database. Consider the following screen shot that shows such an example in action.

image

As you can see there are two ways to select records for deletion:

  • You select checkboxes for rows to be deleted individually.
  • You can check the checkbox placed in the header row to select all the rows. This checkbox toggles the checked state of the other checkboxes.

Once selected you can click on the Delete Selected Customers button to actually delete the records.

Implementing such a functionality is straightforward using ASP.NET MVC, jQuery and Ajax. Let's see how.

As an example we will use Customers table of the Northwind database for this example. You will need to create a model class for the Customers table using EF code first. The Customer class is shown below:

public partial class Customer
{
    [StringLength(5)]
    public string CustomerID { get; set; }

    [Required]
    [StringLength(40)]
    public string CompanyName { get; set; }

    [StringLength(30)]
    public string ContactName { get; set; }

    [StringLength(30)]
    public string ContactTitle { get; set; }

    [StringLength(60)]
    public string Address { get; set; }

    [StringLength(15)]
    public string City { get; set; }

    [StringLength(15)]
    public string Region { get; set; }

    [StringLength(10)]
    public string PostalCode { get; set; }

    [StringLength(15)]
    public string Country { get; set; }

    [StringLength(24)]
    public string Phone { get; set; }

    [StringLength(24)]
    public string Fax { get; set; }
}

The NorthwindDbContext - the DbContext of our model - is shown below:

public partial class NorthwindDbContext : DbContext
{
    public NorthwindDbContext()
        : base("name=NorthwindDbContext")
    {
    }

    public virtual DbSet<Customer> Customers { get; set; }

    protected override void OnModelCreating
                (DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Customer>()
            .Property(e => e.CustomerID)
            .IsFixedLength();
    }
}

Notice that the NorthwindDbContext assumes that the database connection string is stored in web.config with a name of NorthwindDbContext.

Now add HomeController and write Index() and Delete() actions as shown below:

public ActionResult Index()
{
    using (NorthwindDbContext db = 
                  new NorthwindDbContext())
    {
        var query = from c in db.Customers
                    select c;
        return View(query.ToList());
    }
}

public ActionResult Delete(string[] customerIDs)
{
    using (NorthwindDbContext db = 
                        ew NorthwindDbContext())
    {
        foreach (string customerID in customerIDs)
        {
            Customer obj = db.Customers.Find(customerID);
            db.Customers.Remove(obj);
        }
        db.SaveChanges();
        return Json("All the customers 
                     deleted successfully!");
    }
}

The code from the Index() action simply picks all the customers from the Customers table and passes them to the Index view for display.

The Delete() action takes a single parameter - array of CustomerIDs to be deleted. The Delete() action will be called through client side jQuery code and while calling the array will be passed to it. The Delete() action simply iterates through the customerIDs array and one-by-one deletes the customers from the database. Finally, a success message is sent back to the caller in JSON format.

Now add Index view and also add a <script> reference to the jQuery library. Then add the following markup in the Index view.

@model List<SelectAllDeleteDemo.Models.Customer>
...
...
<body>
    <h1>List of Customers</h1>
    <input type="button" id="delete" 
         value="Delete Selected Customers" />
    <br /><br />
    <table border="1" cellpadding="10">
        <tr>
            <th><input type="checkbox" id="checkAll"/></th>
            <th>CustomerID</th>
            <th>CompanyName</th>
            <th>Country</th>
        </tr>
        @foreach(var item in Model)
        {
            <tr>
                <td><input type="checkbox" class="checkBox" 
                     value="@item.CustomerID" /></td>
                <td>@item.CustomerID</td>
                <td>@item.CompanyName</td>
                <td>@item.Country</td>
            </tr>
        }
    </table>
</body>
...

Notice a few things about this markup:

  • The customer data - CustomerID, CompanyName and Country - is displayed in a table.
  • The header row contains a checkbox whose ID is checkAll
  • Each table row contains a checkbox whose class attribute is set to checkBox. And its value is set to the CustomerID of that row.
  • The button above the table is used to initiate the delete operation and its ID is delete.

Now add a <script> block and write the following jQuery code:

$(document).ready(function () {

    $("#checkAll").click(function () {
        $(".checkBox").prop('checked', 
            $(this).prop('checked'));
    });

    $("#delete").click(function () {
        var selectedIDs = new Array();
        $('input:checkbox.checkBox').each(function () {
            if ($(this).prop('checked')) {
                selectedIDs.push($(this).val());
            }
        });

        var options = {};
        options.url = "/home/delete";
        options.type = "POST";
        options.data = JSON.stringify(selectedIDs);
        options.contentType = "application/json";
        options.dataType = "json";
        options.success = function (msg) {
            alert(msg);
        };
        options.error = function () {
            alert("Error while deleting the records!");
        };
        $.ajax(options);

    });
});

The code wires click event handlers for the checkAll checkbox and the delete button. The click event handler of the checkAll checkbox toggles the checked state of all the checkboxes. This is done by selecting the checkboxes using the jQuery class selector. The checkboxes whose class attribute is checkBox are matched and their checked property is toggled. Notice the use of prop() method to do this.

The click event handler of the delete button declares an array variable to store the selected CustomerIDs. It then selects all the checkboxes with CSS class of checkBox. The each() method iterates through these checkboxes. If a checkbox is checked its value is pushed into the array. This way we get all the CustomerIDs into the selectedIDs array. The success callback simply displays the success message returned from the Delete() action.

Then options object is created to hold all the Ajax configuration properties. Notice that url property points to the Delete() action and data property holds the JSON version of the selectedIDs array. Finally, $.ajax() is used to make the Ajax call.

That's it! Run the application and test the functionality.

READ MORE
...