Authenticating MVC Requests

So I decided to sit down today and work the authenticated side of my MVC application.  I started to wonder how the best way to handle scenarios where anonymous users may have access to some actions on a controller and others they must be authenticated.  So I will just show my solution and some of the things I struggled with.

First, I have two Controllers: Main and Series.  The Main controller houses all anonymous content and Series represents on of my model controllers, or a controller that takes care of interaction pertaining to a specific model; in this case the Series model.  In this example, I will show the Login and Logout actions for the Main controller, and the Index action for Series.

So to begin we will write out our two Login actions.  The reason for two is we need a default one when the action is requested that simple shows the Login form with no messages:

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

Next, this is the code for the Login action that actually performs the Login:

   1:  [AcceptVerbs(HttpVerbs.Post)]
   2:  public ActionResult Login(string username, string password)
   3:  {
   4:       if (Membership.ValidateUser(username, password))
   5:       {
   6:            FormsAuthentication.SetAuthCookie(username, false, "/");
   7:            return Redirect(FormsAuthentication.GetRedirectUrl(username, false));
   8:       }
   9:       else
  10:       {
  11:            return View();
  12:       }
  13:  }

Here we are restricting the action to only be invoked when a POST action is in play, that is a form submission.  We use ASP .NET Membership services to perform the login and then, since we are using FormsAuthentication, we must set the cookie.  This caused some confusion for me as I had thought ValidateUser took care of this.  Though when I think about it after the fact it makes sense, from a separation of responsibilities standpoint, that ValidateUser not set the cookie. But the call to SetAuthCookie must be made otherwise the login will be in vain and you wont see the request authenticated in the later portion.

So finally, the Logout action:

   1:  public ActionResult Logout()
   2:  {
   3:       FormsAuthentication.SignOut();
   4:       return RedirectToAction("Index", "Main");
   5:  }

This action, like the default Login action is very simple and straightforward. By calling FormsAuthentication.SignOut we have ASP .NET clean up the session and log the user out. We then simply use a Redirect action result to return to the Main Index page.

So now we have seen how to setup logging in and logging out, but how do you stop the user from access content they are not supposed to.  I have read a number of different ideas on this from deriving the controller base and having a method that is called with each action, to restructuring routing.  The routing idea is tremendously hard to get right and I don’t like the idea of cluttering my actions with a common function.  Remember we also want a way to selectively permit and deny availability of actions based on authorization status.  My choice for this is to decorate prohibited actions with an attribute.  I could easily write my own, derived from ActionFilterAttribute, but there is no need.  Mvc comes with a standard attribute for achieving this called: Authorize.  The following code demonstrates its use to prohibit invocation of an action to anonymous users:

   1:  [Authorize]
   2:  public ActionResult Index()
   3:  {
   4:       return View(_entities.Series.OrderBy(s => s.Name));
   5:  }

As you can see, this is a very concise way to selectively control access to your actions. The Authorize attribute optional also takes parameters to allow you to setup what Roles can access the action if you wanted to take it that far.  Additionally, the attribute can decorate the Controller class itself to prohibit access to all actions.

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
background-color: #f4f4f4;
width: 100%;
margin: 0em;
}
.csharpcode .lnum { color: #606060; }

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s