views:

257

answers:

2

Hello Stackoverflowers,

I am writing my own custom Identity class which implements IIdentity. I don't need to change the default method IsAuthenticated but so now I was wondering how does the default IIdentity determines if it should return true or false?

I thought to find the answer in the FormsAuthenticationTicket I am using but not sure if that is correct.

Thanks in advance,

Pickels

+1  A: 

There is no 'default IIdentity' in the context of an ASP.Net handler.

There is a GenericIdentity that is pass to a GenericPrincipal which is the default User for an ASP.Net handler, and it's behavior is that if it is instantiated with a non-empty username then it is authenticated.

e.g.

public virtual bool IsAuthenticated
{
    get
    {
        return !this.m_name.Equals("");
    }
}

That said, the determination of IsAuthenticated is completely arbitrary and the class implementing IIdentity is fully responsible for implementing this logic.

Typically, there is no use case for instantiating an un-authenticated principal/identity as this is done automatically by the asp.net runtime, thus implementing your custom IIdentity with a 'dumb' IsAuthenticated that returns true should be appropriate in most cases.

Also, while fully implementing IPrincipal and IIdentity is trivial, you could also simply derive from GenericPrincipal and GenericIdentity reducing the amount of code you need to maintain.

In the context of FormsAuthentication you will only have a ticket if the user is authenticated and the User will be an instance of RolePrincipal with an identity of type FormsIdentity and it's implementation of IsAuthenticated is super complex ;-) ...

public bool IsAuthenticated
{
    get
    {
        return true;
    }
}

Hope that helps clear things up.

Sky Sanders
Deriving from GenericIdentity is a good idea. Thanks for taking the time to write this answer.
Pickels
@Pickels - If you plan to use your custom Principal/Identity with FormsAuthentication you may want to investigate the possibility of deriving from RolePrincipal and FormsIdentity as these are the base types expected by all of the built in providers. Unless of course you are implementing your own provider stack and in that case all bets are off and you are free to do as you please.
Sky Sanders
I wont be using any of the providers I think. I am using it with OpenID and trying to store it inside MongoDB. I was doing some tests with IIdentity to easily access some of my user properties(Friendly identifier, ObjectId, email, ...)
Pickels
+1  A: 

I use a custom UserPrinciple to embed more information about the current user into my pages than the standard GenericPrinciple allows. I didn't find a need to implement my own IIdentity as you can easily leverage the built in FormsIdentity similar to my fashion (I'm not sure if this is divergent from standard practices of Auth for .NET it's worked great in practice for myself though). I did create a custom GuestIdentity that returns a hardcoded IsAuthenticated = false perhaps this could be replaced by just GenericPrinciple I'm not sure off hand if it's abstract or not.

public class UserPrincipal : IPrincipal
{            

  private readonly IIdentity _identity;

  public UserPrincipal()
        {
            _identity = new GuestIdentity();

            var guest = //my custom object
            User = guest;
        }        
    public UserPrincipal(HttpContext context)
    {
        var ident = context.User.Identity as FormsIdentity;
        string msg1 = "Context.User.Identity is null for authenticated user.";
        if (ident == null) throw new ApplicationException(msg1);

        _identity = ident;
        string msg2 = "Forms Identity Ticket is null";
        if (ident.Ticket == null) throw new AccessViolationException(msg2);

        var userData = ident.Ticket.UserData;

        ...

        User = jsonSerializer.Deserialize<User>(userJson);
    }    
    #region IPrincipal Members
    public bool IsInRole(string role)
    {
        return User.Roles.FirstOrDefault(x => x.RoleName == role) != null;
    }

    public IIdentity Identity
    {
        get { return _identity; }
    }
    #endregion
}

Random aside, you can cache data in the Forms Authentication ticket like extended UserData, if you follow this type of idea though make sure you have logic in place that can correctly expire stale data since it's stored on the client computer.

Chris Marisic
If I understand correctly you store the userData as Json. Going to use that in my class also it's a great idea.
Pickels
I had thought about pruning more from my code here but I realized it could be useful for others to consider that. At some point I'll probably layer my answer further and cache the result on the server and only deserialize the ticket for an expired cached value.
Chris Marisic
@Pickels - be careful with JSON in the cookie. You need to first UrlEncode the entire JSON string as `,` and others break cookies. Ok, so that is covered, now you have to keep in mind the 4k cookie limit, which, after encryption is about 1.8kb of usable space for the entire cookie, before encryption. Be careful as cookies just get silently truncated. You will be 36 hours in and seeing shadow men before you pwop yourself when you, for no good reason, decode a cookie and find your bug. <-- voice of experience.
Sky Sanders
Thanks for the tips
Pickels
@code poet useful information, the UrlEncoding would most likely explain why when I tried to use BSON it wasn't working, my serialized objects are around 200 bytes so they're very light but I'll keep that in mind if I add more to it.
Chris Marisic