Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make accessing my custom IPrincipal easier in ASP.NET MVC?

I've written a custom principal object which contains a few additional fields (email and userid in addition to the username).

In order to access these properties I have to cast the Context.User object as my custom principal.

@Html.GetGravitarImage((User as CustomPrincipal).Email)

This custom principal is created / deserialized via the Application_AuthenticateRequest in my global.ascx. You can see this question I asked here for more information.

private void Application_AuthenticateRequest(Object source, EventArgs e)
{
    var application = (HttpApplication)source;
    var context = application.Context;

    // Get the authentication cookie
    string cookieName = FormsAuthentication.FormsCookieName;
    HttpCookie authCookie = context.Request.Cookies[cookieName];
    if (authCookie == null)
        return;

    var authTicket = FormsAuthentication.Decrypt(authCookie.Value);
    context.User = CustomPrincipal.CreatePrincipalFromCookieData(authTicket.UserData);
}

However, if a user isn't authenticated, then my cast to CustomPrincipal will fail (because it won't be injected in the method above) and the result of the (User as CustomPrincipal) will return null, thus giving me a null reference exception when my method above attempts to get the email.

What would be a clean solution to this problem? I want to make accessing my custom principal easy and having to do the following seems cumbersome:

@Html.GetGravitarIcon((User is CustomPrincipal) ? (User as CustomPrincipal).Email : "Default Email")

Is this the only way to handle this situation?

like image 916
Viktor Avatar asked Aug 12 '11 18:08

Viktor


People also ask

What is authentication filter in MVC?

Authentication Filter is a new feature in MVC 5 this filter run before any other filter, this filter is used to authenticate User which was not there in older version [MVC 4] there we were using Authorization filter or Action filter to Authenticate User, now new updated of MVC 5 this cool feature is available.

What can I do with ASP NET MVC?

Based on ASP.NET, ASP.NET MVC allows software developers to build a web application as a composition of three roles: Model, View and Controller. The MVC model defines web applications with 3 logic layers: Model (business layer) View (display layer)


1 Answers

I whipped something together quickly. One possible way of easily introducing a custom IPrincipal in ASP.NET MVC is the following:

1) Create your own descendant of the IPrincipal interface.

public interface IMyPrincipal : IPrincipal
{
    Guid UserId { get; }
    string EmailAddress { get; }
}

2) Let's assume you are using the ASP.NET Membership provider to authenticate your users. Let's quickly build an IMyPrincipal implementation which utilizes the membership API.

public class MyPrincipal : IMyPrincipal
{
    private MembershipUser _user;

    public MyPrincipal()
    {
        this._user = Membership.GetUser();
        var userName = this._user != null ? this._user.UserName : String.Empty;
        this.Identity = new GenericIdentity(userName);
    }

    public Guid UserId
    {
        get 
        { 
            return this._user != null ? (Guid) this._user.ProviderUserKey : 
                                        default(Guid);
        }
    }

    public string EmailAddress
    {
        get 
        { 
            return this._user != null ? this._user.Email : null;
        }
    }

    public IIdentity Identity { get; private set; }
    public bool IsInRole(string role) { return false; }
}

3) Create your own base class type for your controllers. Hide the inherited User member and introduce your own IPrincipal descendant.

public class BaseController : Controller
{
    protected virtual new MyPrincipal User
    {
        get { return HttpContext.User as MyPrincipal; }
    }
}   

4) Have all your controllers descend from this new BaseController type.

public class HomeController : BaseController
{
  //...
}

5) Create your own controller factory to make sure your principal is introduced on the HttpContext / Thread.

public class MyControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance
        (RequestContext requestContext, Type controllerType)
    {
        try
        {
            var controller = base.GetControllerInstance(requestContext, controllerType);
            requestContext.HttpContext.User = Thread.CurrentPrincipal = new 
                                                    MyPrincipal();
            return controller;
        }
        catch (Exception)
        {
            return base.GetControllerInstance(requestContext, controllerType);
        }
    }
}

6) Register the controller factory in the Global.asax's Application_Start() event handler.

var controllerFactory = new MyControllerFactory();
ControllerBuilder.Current.SetControllerFactory(controllerFactory);

Voila, now you can use the new User (IMyPrincipal) anywhere in your controllers.

For example:

public ActionResult Index()
{
    ViewBag.Message = "Welcome to ASP.NET MVC!";

    ViewBag.UserId = User.UserId;
    ViewBag.UserName = User.EmailAddress;

    return View();
}
like image 69
Christophe Geers Avatar answered Oct 20 '22 05:10

Christophe Geers