views:

846

answers:

4

Hello mates, I'm using ASP.NET MVC and Forms Authentication on my application. Basically I use FormsAuthentication.SetAuthCookie to login and FormsAuthentication.SignOut to logout.

In the HttpContext.Current.User.Identity I have stored the user name but I need more info about the logged user. I don't want to store my entire User obj in the Session because it might big and with much more than I need.

Do you think it's a good idea to create like a class called LoggedUserInfo with only the attributes I need and then add itto the session? Is this a good approach?

Or do you have better ideas?

Thanks for that! G

A: 

Well you will have to store these somewhere. Two main possible places though:

The server

You can either put them into Session. I suggest you do create a separate class that will hold only data that you actually need to avoid of wasting too much memory. Or you can also store into Cache that can end up in having many DB calls when there are huge amounts of concurrent users.

The client

In this case if you can limit the amount of data with a separate class, to that and use whatever way to serialize it and send it to the client. Either in a cookie or in URI (if length permits and cookies are disabled)...

Outcome of these thoughts:
the main thing here would be to create a separate class if you gain much memory resources this way. So that's the first thing you should do.

Robert Koritnik
+1  A: 

Store it server side in the session.

Eg.

// Make this as light as possible and store only what you need
public class UserCedentials
{
    public string Username { get; set; }
    public string SomeOtherInfo { get; set; }
    // etc...
}

Then when they sign in just do the following to save the users info:

// Should make typesafe accessors for your session objects but you will
// get the point from this example
Session["UserCredentials"] = new UserCredentials()
    { Username = "SomeUserName", SomeOtherInfo = "SomeMoreData" };

Then whenever you need it fetch it:

UserCredentials user = (UserCredentials)(Session["UserCredentials"]);

I have written a couple of question/answers regarding doing custom authorization in MVC: http://stackoverflow.com/questions/1151450/how-to-implement-authorization-checks-in-asp-net-mvc-based-on-session-data

http://stackoverflow.com/questions/1350561/how-does-the-authorize-tag-work-asp-net-mvc/

Kelsey
Storing authentication-related info in session is *dangerous.* Session is decoupled from membership. Google "ASP.NET session stealing".
Craig Stuntz
+3  A: 

I use this solution:

ASP.NET 2.0 Forms authentication - Keeping it customized yet simple

To summarize: I created my own IPrincipal implementation. It is stored in HttpContext.Current.Cache. If it is somehow lost, I have username from client side authorization cookie and can rebuild it. This solution doesn't rely on Session, which can be easily lost.

EDIT

If you want to use your principal in your controller and make it testable, you can do this:

    private MyPrincipal _myPrincipal;
    MyPrincipal MyPrincipal
    {
        get
        {
            if (_myPrincipal == null)
                return (MyPrincipal)User;
            return _myPrincipal;
        }
        set
        {
            _myPrincipal = value;
        }
    }

In your test, you will set object prepared for testing. Otherwise it will be taken from HttpContext. And now I started thinking, why do I use Ninject to do it?

LukLed
I use the same approach more or less. I'd rather store the user ID (PK on your DB table) instead of the user NAME.
synhershko
In my application the "login" is a key as well with a index so there is no problem. My question about storing the IPrincipal in the cache is if it results in a better performance and reliable solution compared to storing my "UserCredentials" into the session.
Guillermo Guerini
@Guillermo Guerini: Do you really have that big application that this performance could be a problem? Using Session means using additional cookie (additional to forma authentication cookie) or dangerous cookieless solution. I don't see performance difference, but using Session can give you additional problems.
LukLed
@LukLed5: Actually I'm tending to go with your solution but I was just wondering if it results in a bed performance. I agree with you about all the session problems. Well, thank you very much. I'll try your idea. Thank you very much.
Guillermo Guerini
@Guillermo Guerini: I don't know, but there will be propably no noticable performance difference. You'll be able to turn off session state. It can save you some processor cycles:)
LukLed
@LukLed: Nice! Last question. In your solution you convert the HttpContext.Current.User.Identity to FormsIdentity. Do you have a reason for doing this?
Guillermo Guerini
@LukLed5: Well, everything is working fine here! Very good your solution. Now what do you suggest to access this info in my controllers and views? Do you think I should create a wrapper into something like MyController to make the access easier? And to my views?
Guillermo Guerini
@Guillermo Guerini: Here you can read about forms authentication: http://msdn.microsoft.com/en-us/library/aa480476.aspx From the page: Forms authentication uses an authentication ticket that is created when a user logs on to a site, and then it tracks the user throughout the site. The forms authentication ticket is usually contained inside a cookie. The FormsAuthenticationModule class constructs a GenericPrincipal object and stores it in the HTTP context. The GenericPrincipal object holds a reference to a FormsIdentity instance that represents the currently authenticated user.
LukLed
@LukLed Thanks for the link. I'll take a read. And about how accessing the MyPrincipal properties through the application? Do you have a good idea? Thank you very much for your help. ;)
Guillermo Guerini
@Guillermo Guerini: First I defined IMyPrincipal which inherited from IPrincipal. Then I implemented it in MyPrincipal class. My BaseController class has IMyPrincipal MyPrincipal property, which is injected by my DI container. I use Ninject, so it looks like `Bind<IMyPrincipal>().ToMethod(c => (IMyPrincipal)HttpContext.Current.User).OnlyIf(a => HttpContext.Current.User is IMyPrincipal);`
LukLed
@LukLed Is there any other way besides Ninject? I've never used it before so I don't know where to start. I thought I could create a BaseController for example and create the object there like: "MyPrincipal principal = (MyPrincipal)HttpContext.Cache.Get(User.Identity.Name);" So it would be accessible to all the Controllers that inherited from the BaseController but I can't do that because of the HttpContext. Ideas?
Guillermo Guerini
@Guillermo Guerini: Look at my edit on answer.
LukLed
+1  A: 

I actually like to use a CustomPrincipal and CustomIdentity which I set in the logon action method like

        if (!String.IsNullOrEmpty(username) && !String.IsNullOrEmpty(password) && _authService.IsValidLogin(username, password))
        {
            User objUser = _userService.GetUserByName(username);
            if (objUser != null)
            {
                //** Construct the userdata string
                string userData = objUser.RoleName + "|" + objUser.DistrictID + "|" + objUser.DistrictName + "|" + objUser.ID + "|" + objUser.DisplayName;
                HttpCookie authCookie = FormsAuthentication.GetAuthCookie(username, rememberMe.GetValueOrDefault());
                FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(authCookie.Value);
                FormsAuthenticationTicket newTicket = new FormsAuthenticationTicket(ticket.Version, ticket.Name, ticket.IssueDate, ticket.Expiration, ticket.IsPersistent, userData);
                authCookie.Value = FormsAuthentication.Encrypt(newTicket);
                Response.Cookies.Add(authCookie);
                return RedirectToAction("Index", "Absence");
            }
            else
            {
                return RedirectToAction("LogOn", "Account");
            }
        }
        else
        {
            return RedirectToAction("LogOn", "Account");
        }

Then in the custom principal you can have methods that access specific information you passed in to the constructor like

((CustomIdentity)((CustomPrincipal)HttpContext.Current.User).Identity).DisplayName;

where the DisplayName property is declared in the CustomIdentity class.