0
votes

After researching FormsAuthentication for a few days, I decided to store a serialized object in the FormsAuth cookie's UserData property and use a custom IPrincipal object for the HttpContext.Current.User.

Most of the guides I've found say to cast the IPrincipal object to your object. I get an invalid cast exception every time though. What am I doing wrong?

MyUserData

public class MyUserData
{
    public long UserId { get; set; }
    public string Username { get; set; }
    public bool IsSuperUser { get; set; }
    public string UnitCode { get; set; }
    public string EmailAddress { get; set; }
    public List<string> Roles { get; set; }

    // Serialize    
    public override string ToString()
    {
        JavaScriptSerializer serializer = new JavaScriptSerializer();
        string result = serializer.Serialize(this);
        return result;
    }

    // Deserialize
    public static MyUserData FromString(string text)
    {
        JavaScriptSerializer serializer = new JavaScriptSerializer();
        return serializer.Deserialize<MyUserData>(text);
    }
}

CustomPlatformPrincipal

public class MyCustomPrincipal : IPrincipal
{
    public MyUserData MyUserData { get; set; }
    public IIdentity Identity { get; private set; }

    public MyCustomPrincipal(MyUserData myUserData)
    {
        MyUserData = myUserData;
        Identity = new GenericIdentity(myUserData.Username);
    }

    public bool IsInRole(string role)
    {
        return MyUserData.Roles.Contains(role);
    }
}

Global.asax.cs

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
    {
        HttpCookie authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName];
        if (authCookie == null || authCookie.Value == "")
        {
            return;
        }

        FormsAuthenticationTicket authTicket;
        try
        {
            authTicket = FormsAuthentication.Decrypt(authCookie.Value);
        }
        catch
        {
            return;
        }

        if (Context.User != null)
        {
            // the from string deserializes the data
            MyUserData myUserData = MyUserData.FromString(authTicket.UserData);
            Context.User = new MyCustomPrincipal(myUserData);
        }
    }

My Page

var myUserData = ((MyCustomPrincipal)(HttpContext.Current.User)).MyUserData;
// invalid cast exception (can't cast IPrincipal to MyCustomPrincipal)

Article I was following: http://primaryobjects.com/CMS/Article147.aspx

So it seems the only way I could get my data is to decrypt the auth cookie, then deserialize the authCookie's userData string.

Any suggestions?

Update

Tried following the suggestions on this SO question: Implementing a Custom Identity and IPrincipal in MVC

Code is below, but it didn't work.

[Serializable]
public class MyCustomPrincipal : IPrincipal, ISerializable
{
    public CustomUserData CustomUserData { get; set; }
    public IIdentity Identity { get; private set; }

    //public MyCustomPrincipal (IIdentity identity) { Identity = identity; }

    public MyCustomPrincipal(CustomUserData customUserData)
    {
        CustomUserData = customUserData;
        Identity = new GenericIdentity(customUserData.Username);
    }

    public bool IsInRole(string role)
    {
        return PlatformUserData.Roles.Contains(role);
    }


    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        if (context.State == StreamingContextStates.CrossAppDomain)
        {
            MyCustomPrincipal principal = new MyCustomPrincipal (this.CustomUserData );
            info.SetType(principal.GetType());

            System.Reflection.MemberInfo[] serializableMembers;
            object[] serializableValues;

            serializableMembers = FormatterServices.GetSerializableMembers(principal.GetType());
            serializableValues = FormatterServices.GetObjectData(principal, serializableMembers);

            for (int i = 0; i < serializableMembers.Length; i++)
            {
                info.AddValue(serializableMembers[i].Name, serializableValues[i]);
            }
        }
        else
        {
            throw new InvalidOperationException("Serialization not supported");
        }
    }
}
1
That article was not useful to me. I'm on a tight deadline, I'm using Forms Authentication and all I need to know is how to get MyCustomPrinciple out of the HttpContext.Current.User. I've already taken care of everything else (login, errors, etc.).Lifes
Deleted my answer below as it wasn't relevant. The only other thing I can think of is to export the HttpContext.Current.User to a var var thisUser = HttpContext.Current.User then try and cast from the new variable.Anthony Nichols
Another long shot but you also might try inheriting from System.Security.Principal.IPrincipal just to insure you are inheriting from the correct class.Anthony Nichols
The code that you've posted isn't enough to actually do anything with. For instance, what does my MyUserData look like? In addition, what type do you expect "var myUserData" to be?David L
That didn't work either, and I'm already inheriting from IPrinciple.Lifes

1 Answers

2
votes

Did you run in the debug mode? You can put break point on HttpContext.Current.User, you will see what type the user was at that moment. And from your Application_AuthenticateRequest method, there is no guarantee that the User will be your expected type. There are many exit points before reaching your custom type setup. Even this code: Context.User != null. It was wrong with your expectation. I have not gone through the detail of the Context.User, however, in term of your context, you were expecting the Context.User was your custom user. So the valid check should be:

var custom = Context.Current as MyCustomPrinciple;
if(custom == null)
{
// Your construct code here.
}

My strongly suggestion is: you need to go in debug mode, to see exactly what was going on.