Question

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");
        }
    }
}
Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top