Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom IPrincipal Not Accessible in MVC5

I've read a lot of questions and answers on this topic, but none of them have helped me solve this issue.

The problem I'm having is that the HttpContext.Current.User or just User property is of type RolePrincipal instead of my custom principal.

This is an MVC 5 web app using Windows Authentication (intranet only application). My custom principal is a subclass of WindowsPrincipal and I did implement my own RoleProvider for use in Authorize attribute tags.

When I attempt to use the principal by casting it to my custom principal from IPrincipal on the current HttpContext I get an error stating that it's of type RolePrincipal, which obviously cannot be casted to my custom principal. I am setting my custom principal in the Application_PostAuthenticationRequest event:

protected void Application_PostAuthenticationRequest(object sender, EventArgs e)
{
    if (User == null)
        return;

    using(EntityContext db = new EntityContext ())
    {
        var user = db.Users.SingleOrDefault(u => u.ADName.Equals(User.Identity.Name));
        HttpContext.Current.User = new PcsPrincipal((WindowsIdentity)User.Identity, user);
    }
}

When I put a breakpoint in that method it appears to never get called, which may explain why it's not getting set to my custom principal.

I've reviewed the following QAs, but they have not been able to resolve the issue:

  • using custom IPrincipal and IIdentity in MVC3 - Added the registration of the event handler in Global.asax.cs's Init method. Didn't seem to make a difference. Also the _application variable appeared to be invalid in MVC5.
  • RoleProvider dosn't work with custom IIdentity and IPrincipal on server - Made the changes in the web.config file, but this caused various errors during runtime with errors saying that the ASP.NET pipeline is configured incorrectly.

What am I doing incorrectly to not have the Principal set? Let me know if more code needs to be posted.

EDIT: Setting the HttpContext.Current.User to my custom principal in the WindowsAuthentication.OnAuthenticate event does not resolve this issue. The exact same behavior is exhibited using that method.

like image 716
JNYRanger Avatar asked Oct 31 '22 17:10

JNYRanger


2 Answers

You should set the custom principal in WindowsAuthentication_OnAuthenticate event which occurs when the application authenticates the current request.

protected void WindowsAuthentication_OnAuthenticate(object source, WindowsAuthenticationEventArgs e)
{   
    using(EntityContext db = new EntityContext ())
    {
        var user = db.Users.SingleOrDefault(u => u.ADName.Equals(e.Identity.Name));
        HttpContext.Current.User = new PcsPrincipal(e.Identity, user);
    }
}
like image 186
user1672994 Avatar answered Nov 08 '22 06:11

user1672994


After continuously researching this issue I finally found the answer by means of another SO question making my question sort of a duplicate: MVC3 Windows Authentication override User.Identity

Below is the answer posted by @Toby Jones (as an edit to his original question) which lead to my resolving my issue, but his answer is actually the aggregation of two answers posted by @Erik Funkenbusch & @Darin Dimitrov. The answer is edited to fix some grammar & to remove some superfluous information.

Option 1: Override the Authorize Request in Global.asax

The Application_AuthenticateRequest event should not be used because (HttpContext.Current.User is null even though Windows Authentication is on) the user has not been populated in the Windows authentication process, and thus there is nothing that I can use to get the user information.

Application_AuthorizeRequest is the next in the chain and happens after the WindowsIdentity is brought in.

protected void Application_AuthorizeRequest(object sender, EventArgs e)
{
    if (User.Identity.IsAuthenticated && Roles.Enabled)
    {
        Context.User = new CustomPrincipal((WindowsIdentity)User.Identity);
    }
}

Option 2: Override the AuthorizeAttribute

Here is the override of the Authorize Attribute

public class CAuthorize : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        bool authorized = base.AuthorizeCore(httpContext);
        if (!authorized)
            return false;

        IIdentity user = httpContext.User.Identity;
        CPrincipal cPrincipal = new CPrincipal(user);
        httpContext.User = cPrincipal;

        return true;
    } 
}

Then replace all AuthorizeAttributes to the custom version.

Option 1 handles everything globally, while Option 2 handles everything at a more individual level using filters.

Personally, I chose to use the global.asax method so I have my custom principal available globally. Here's the actual code that resolved my issue:

protected void Application_AuthorizeRequest(object source, EventArgs e)
{
    if(User.Identity.IsAuthenticated && Roles.Enabled)
    {
        using (EntityContext db = new EntityContext ())
        {
            var user = db.Users.Include("Roles").SingleOrDefault(u => u.ADName.Equals(User.Identity.Name));
            if (user == null)
                return;

        PcsPrincipal principal = new PcsPrincipal((WindowsIdentity)User.Identity, user);
            Context.User = principal;
        }
    }            
}
like image 30
JNYRanger Avatar answered Nov 08 '22 06:11

JNYRanger