Chaddeus
Chaddeus

Reputation: 13356

How to implement IIdentity for a custom User object in ASP.NET MVC?

In my ASP.NET MVC app, I'm trying to create a custom HttpContent.User object. I've started by creating a Member class, which implements IPrincioal.

public class Member : IPrincipal
{
    public string Id { get; set; }
    public IIdentity Identity { get; set; }
    public bool IsInRole(string role) { throw new NotImplementedException(); }
    ...
}

Then at authentication time I set HttpContext.User to an instance of a Member class:

FormsAuthentication.SetAuthCookie(email, false);
HttpContext.User = member;

Then later I want to check if the user is authenticated, like so:

if (User.Identity.IsAuthenticated) { ... }

That's where I'm stuck. I'm not sure what I need to do for the public IIdentity Identity property on the instance of the Member. So that I can use the HttpContext.User object something like this:

IsAuthenticated = HttpContext.User.Identity.IsAuthenticated;
ViewBag.IsAuthenticated = IsAuthenticated;

if (IsAuthenticated) {
    CurrentMember = (Member)HttpContext.User;
    ViewBag.CurrentMember = CurrentMember;
}

Upvotes: 4

Views: 2382

Answers (1)

danludwig
danludwig

Reputation: 47375

A Principal is not something you can just set once when writing the auth cookie and forget later. During subsequent requests, the auth cookie is read and the IPrincipal / IIdentity is reconstructed before executing an action method. When that happens, trying to cast the HttpContext.User to your custom Member type will throw an exception.

One option would be to intercept in an ActionFilter, and just wrap the standard implementation.

public class UsesCustomPrincipalAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var systemPrincipal = filterContext.HttpContext.User;
        var customPrincipal = new Member(systemPrincipal)
        {
            Id = "not sure where this comes from",
        };
        filterContext.HttpContext.User = customPrincipal;
    }
}

public class Member : IPrincipal
{
    private readonly IPrincipal _systemPrincipal;

    public Member(IPrincipal principal)
    {
        if (principal == null) throw new ArgumentNullException("principal");
        _systemPrincipal = principal;
    }

    public string Id { get; set; }

    public IIdentity Identity { get { return _systemPrincipal.Identity; } }

    public bool IsInRole(string role)
    {
        return _systemPrincipal.IsInRole(role);
    }
}

This way, you're not losing anything that comes out of the box with the default IPrincipal and IIdentity implementations. You can still invoke IsAuthenticated on the IIdentity, or even IsInRole(string) on the IPrincipal. The only thing you're gaining is the extra Id property on your custom IPrincipal implementation (though I'm not sure where this comes from or why you need it).

Upvotes: 6

Related Questions