views:

172

answers:

2

I know similar questions have been posed before, but I could not find a duplicate that handles ASP.NET MVC (and the controller aspect) specifically.

My question is as follows:

In a MVC model, my understanding is that the controller should handle the use of HttpContext to determine who is logged on, if any. This is so the controller can present this information to the view, so that the view itself does not have to perform these look-ups.

Is there any defacto standard on how to do it?

My current setup is as follows [simplified]

I have a BaseController, which all my other controllers inherit.

In BaseController, I perform the following override:

protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
     base.Initialize(requestContext);
     ViewData["IsUserLoggedIn"] = IsUserLoggedIn; // bool property checking HttpContext.User.Identity.IsAuthenticated;
     ViewData["CurrentUser"] = CurrentUser; // Property returning logged in user, or null.

}

Then, naturally, in my Views I can check the ViewData values.

How do you do it? I'd like to know if something is wrong with my setup, and if so, what's wrong? I'm not 100% comfortable with my solution, - mainly because I'm not too familiar with the cycle, and I'm uncertain whether I've placed my code at the correct place.

I'm aware there might not be "one answer binds them all" here, I'm accepting the answer that offers most insight.

+2  A: 

I put an encrypted element into a cookie usually. That element can be anything, but I generally make it the username.

Then in Global.asax.cs I implement Application_AuthenticateRequest. Each time a page is loaded this method is called by the system. In that method I check the cookie, if it exists, I try to load up the user. If the user loads successfully, then I know I have a logged in user, and I set the current Threads current Principal property to that user.

Once the CurrentPrincipal is set on the current thread you can access that user from your Controller, your View, your Business Layer, anywhere in your execution path.

If I can't use cookies for some reason, then I'll pass it across in ViewData (again encrypted just in case), and store it in hidden variables. Then when Controller::OnActionExecuting runs I do the same work I typically did in AuthenticateRequest (i.e. load the user and put it on the thread).

Arthur C
Interesting approach. How does your cookie scheme relate to the FormsAuthentication cookie (if you are using Forms Authentication, of course) ?
Terje
I'm not using FormsAuthentication, but I do use a regular FormsAuthenticationTicket and HttpCookie.When a user tries to login, my controller first calls into my domain layer to try to login the user. If that succeeds then my controller creates a FormsAuthenticationTicket object containing my encrypted user name, places it into an HttpCookie, and puts that cookie into the Response.Cookies collection.In that past I've also had to add something to the system to track of the user was actually logged in (i.e. you load the user from the ticket, then check if they were logged in as well).
Arthur C
I was looking for insight, and I got some. Thanks. I'll look into this approach and see from my own experience how it compares to what I currently use. Tracking whether users are logged in or not; I handle that with a last-activity stamp in my user table, updating it each time BaseController's Initialize is called.
Terje
A: 

I have a BaseController class that all my controllers inherit from. It has a "CurrentUser" property that is stored in the session. The full code for my controller has a little more logic for retrieving the user, but this is the basic idea.

public class BaseController : Controller
{

User _currentUser = null;
public User CurrentUser
{
    get
    {
        if (_currentUser == null)
            _currentUser = (User)HttpContext.Session["CurrentUser"];

        return _currentUser;

    }
    set
    {
        _currentUser = value;
        HttpContext.Session["CurrentUser"] = value;
        }
    }
}


}

My models all inherit from a BaseModel class which also has a CurrentUser property.

public class BaseModel
{
    public User CurrentUser { get; set; }
}

public class HomeIndexData : BaseModel
{

}

Then my controllers pass the user to the model which allows me to have strongly-typed views.

[HttpGet]
public ActionResult Index()
{
    HomeIndexData data = new HomeIndexData();
    data.CurrentUser = this.CurrentUser;

    return View(data);
}

With this technique, I can also have a strongly-typed master page using BaseModel.

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage<BaseModel>" %>
Pete Nelson
I've used a similar approach on an earlier project. Mostly it worked well, but in some cases it did not make sense [or it was not possible] to extend BaseModel. (Imagining mapping to DataTable classes directly, or using model objects from external project .dll files, for example). So I ended up with ViewData.Also, you use Session for your CurrentUser property. Is there a [good] reason for this? For example, why could you not use Controller.HttpContext.User.Identity ? Or maybe a better question is, why do you think that is a worse idea?
Terje
Having a custom user object rather than User.Identity makes it a little more extensible in our situation. For example, our master page has a section that displays the user's first and last name, something we can't do with just User.Identity.Name.Personally, if I have items in a DataTable that the app needs, I'd much rather make a POCO and populate it with the DataTable.
Pete Nelson