ASP.Net MVC Framework - Using Forms Authentication

NOTE: THIS Post was written when ASP.NET MVC Framework was in a early Preview stage. 

 

This post is about using Forms Authentication with the MVC Framework. I only want to let you know that this is a simple and fast “hack” only to show a simple solution to get it work. I decided to use the Membership feature for the validation of user name and password.

The first thing we need to do is to enable Forms Authentication and specify a Login Url, I decided to use the Login URL in a MVC “friendly” format:

 

<authentication mode="Forms">
   <forms loginUrl="Login/Login"></forms>
</authentication>

 

The value of the loginUrl has some redundant value, Login/Login, but this is because the first Login points to the LoginController and the last Login to the Action method Login in the LoginController.

The next thing we need to do is to make sure anonymous users don’t have access to our site, this is done by adding the authorization section to the web.config:

 

<authorization>
     <deny users="?"/>
</authorization>

 

If a user now try to get access to our site, he/she will be redirected to the ”Login/Login” URL, this will happen if we use the following URL format in the RouteTable:


Url = "[controller]/[action]"


A call to the Login Action method of the LoginController will take place.

In the Views folder we can add our Login View (We will look at the LoginController later in this post).


/Views/Login/Login.aspx


The following is a simple Login View:

 

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Login.aspx.cs" Inherits="MvcApplication4.Views.Login.Login" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >

<head runat="server">
    <title>Untitled Page</title>
</head>

<body>
    <div>
        <% if (this.ViewData.ContainsDataItem("LoginFaild")) { %>
        <div style="color:Red"><%=this.ViewData["LoginFaild"] %></div>
        <% } %>

        <form method="post" action="/Login/Login">
        User name: <input type="text" name="userName" /><br />
        Password:&nbsp; <input type="password" name="password" /><br /><br />
        <input type="submit" value="Login" />
        </form>

    </div>

</body>
</html>

 

If the ViewData in will contain an data item with the name LoginFaild it will display the value added to the LingFaild data item, in this case a message like “Login faild….”

The <form> element’s action method will make sure the LoginController’s Login method will be called when the user hit the Login submit button. As you can see the same URL specified for the FormsAuthentication’s loginUrl is used here. It’s because the <location> section in the web.config will not work properly with the MVC Framework "friendly" URL, so if we try to use another URL like “/Login/Authenticate” etc, we will be redirected to the “Login/Login” URL because the <authorization> section denies all anonymous user to access any URL in our app, besides the specified loginUrl.

Note: The <location> will still work for CSS files etc. So to enable a css style when we deny anonymouse use to access "files" in our web site, we can add the <location> to make sure CSS files and images etc can be requested.


  <location path="Stylesheet1.css">
    <system.web>
      <authorization>
        <allow users="*"/>
      </authorization>
    </system.web>
  </location> 

 

The username and password input fields values will be passed as arguments to the Login action of the LoginController. Here is the implementation of the LoginController and its Login action:

 

    public class LoginController : Controller
    {
        [ControllerAction]
        public void Login(string userName, string password, string ReturnUrl)
        {
            if (this.IsValidLoginArgument(userName, password))
            {
                if (Membership.ValidateUser(userName, password))
                    this.RedirectFromLoginPage(userName, ReturnUrl);
                else
                    this.ViewData["LoginFaild"] = "Login faild! Make sure you have entered the right user name and password!";
            }

            RenderView("Login");
        }

        private void RedirectFromLoginPage(string userName, string ReturnUrl)
        {
            FormsAuthentication.SetAuthCookie(userName, false);

            if (!string.IsNullOrEmpty(ReturnUrl))
                Response.Redirect(ReturnUrl);
            else
                Response.Redirect(FormsAuthentication.DefaultUrl);
        }

        private bool IsValidLoginArgument(string userName, string password)
        {
            return !(string.IsNullOrEmpty(userName) && string.IsNullOrEmpty(password));
        }
    }

 

As you can see the Login action method takes three arguments, the userName, password and ReturnUrl. The value for the userName and password, will came from the input fields located on the Login View, the ReturnUrl is a query string used by the FormsAuthentication, so the value of the ReutnrUrl argument will came from the ReturnUrl query string. If we have input fields with the same name as the arguments of an action method in the View, the value of the fields will be “mapped” to the argument of the action method with the same name as the field, the same thing with query strings.

Note: There is a bug in the current CTP of the MVC Framework, so the value of a query string will not always be passed to the action method’s arguments.

The firs lines of code in the Login action method will check if the username and password aren’t empty. The check is used to make sure the Login View will be displayed when FormsAuthentication will redirect to the LoginURL “Login/Login”. If a user tries to enter and URL and the user is an anonymous user (Not authenticated), the Login Action method of the LoginController will be executed. In this case the username and password argument will be null, and the Login View will be rendered. When the Login view is displayed, the user can now enter a username and a password and hit the Login button. When he/she have done that, the LoginController’s Action method will be called, and this time the username and password will not be empty, so the Membership feature will validate the user, if he/she is valid, the Authentication Cookie used by the FormsAutehtnicon will be set, and the user will be redirected to the URL he/she have requested, and the user is now authenticated and have access to the site.

If the user enter a invalid user name or password, the Login View will be displayed and the “Login faild!.....” text added to the ViewData will be displayed.

This was a simple solution to use the Forms Authentication, there are some other options where can simply create our own RouteHandler which can handle the authentication check and redirect to a login View, or we can handle it on a Controller level. I will probably write a blog post about that later.

I have another post about the MVC Framework and security: http://weblogs.asp.net/fredriknormen/archive/2007/11/25/asp-net-mvc-framework-security.aspx. The post is created on the pre-CTP of the MVC Framework, so have that in mind, but basically the solution is the same.

10 Comments

  • Nice it looks like a good solution. I will use Fredrik, isn't there a change to use colour syntax for the code in your blog? Then it will be much easier to follow.


  • Gustav:
    Well I can't find it, so I used my old blog to add color, but this time I have problem accessing it. Some people use a tool to add the color, I will see if I will find one that I like, if you have a tip, please let me know.

  • Something you need to be aware of is that if you disallow access to everything for anonymous users, they might not have the permissions required to grab the stylesheets and and layout images you use so you'll need to seperately allow all users to access those resources or your pages can lose all formatting.


  • Garry Shutler:
    Thanks for pointing this out, I should have added it to my post. But I hope they see your comment.
    Edit: I have now add it to my post.

  • Thanks for the great posting.
    Would you more elaborate the bug with query string not always be passed to the action method's arguments or pointing me url where I can find more information?

  • Isn't aspect oriented way(that is using attributes) is better for this type of scenario?

  • Sorry I wasnt aware that current CTP didn't support it.

  • For those who are using MvcContrib can achieve it by using Filters. In the execute method, checking for the current user and the role may help. With the help of rescue, you may even achieve nice looking errors with more clean design.

  • Any way to update this article to work with MVC Preview 3?

    Thanks,
    Tony

  • Hello,

    How to access user information from the asp page out of the login cookie?

Comments have been disabled for this content.