I trying to add custom properties to the ApplicationUser for a web site using MVC5 and OWIN authentication. I've read https://stackoverflow.com/a/10524305/264607 and I like how it integrates with the base controller for easy access to the new properties. My issue is that when I set the HTTPContext.Current.User property to my new IPrincipal I get a null reference error:
[NullReferenceException: Object reference not set to an instance of an object.]
System.Web.Security.UrlAuthorizationModule.OnEnter(Object source, EventArgs eventArgs) +127
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +136
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +69
Here is my code:
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
ApplicationUser user = userManager.FindByName(HttpContext.Current.User.Identity.Name);
PatientPortalPrincipal newUser = new PatientPortalPrincipal();
newUser.BirthDate = user.BirthDate;
newUser.InvitationCode = user.InvitationCode;
newUser.PatientNumber = user.PatientNumber;
//Claim cPatient = new Claim(typeof(PatientPortalPrincipal).ToString(), );
HttpContext.Current.User = newUser;
}
}
public class PatientPortalPrincipal : ClaimsPrincipal, IPatientPortalPrincipal
{
public PatientPortalPrincipal(ApplicationUser user)
{
Identity = new GenericIdentity(user.UserName);
BirthDate = user.BirthDate;
InvitationCode = user.InvitationCode;
}
public PatientPortalPrincipal() { }
public new bool IsInRole(string role)
{
if(!string.IsNullOrWhiteSpace(role))
return Role.ToString().Equals(role);
return false;
}
public new IIdentity Identity { get; private set; }
public WindowsBuiltInRole Role { get; set; }
public DateTime BirthDate { get; set; }
public string InvitationCode { get; set; }
public string PatientNumber { get; set; }
}
public interface IPatientPortalPrincipal : IPrincipal
{
WindowsBuiltInRole Role { get; set; }
DateTime BirthDate { get; set; }
string InvitationCode { get; set; }
string PatientNumber { get; set; }
}
I haven't found much in the way of documentation on how to do this, I've read these articles:
The comments in the second link pointed me to perhaps using claims (http://msdn.microsoft.com/en-us/library/ms734687.aspx?cs-save-lang=1&cs-lang=csharp) , but the article linked to doesn't show how to add those to an IPrincipal
(which is what HttpContext.Current.User
is), or where in the pipeline you're supposed to add them to a ClaimsIdentity
(which is the concrete class the User
is). I'm leaning towards using claims, but I need to know where to add these new claims to the user.
Even if claims are the way to go, I'm curious as to what I'm doing wrong with my custom IPrincipal, as I seem to have implemented everything it requires.
You're getting an exception because
HttpContext.Current.User.Identity.IsAuthenticated
returns false at the point of check (so doesHttpContext.Current.Request.IsAuthenticated
).If you remove the
if (HttpContext.Current.User.Identity.IsAuthenticated)
statement it will work fine (at least this part of code).I've tried a simple thing like this:
BaseController.cs
CustomPrincipal.cs
Global.asax.cs
HomeController.cs
And this is working fine. So unless this code:
is not returning a valid (custom) user object, the problem is with the
if()
statement.Your update looks fine, and if you're happy to store data as claims in a cookie you can go with it, although I personally hate the
try {}
catch block there.What I do instead is this:
BaseController.cs
I decorate the base controller class with a custom attribute.
AuthorizeExAttribute.cs:
And WorkContext.cs:
I then access the WorkContext like this:
I'm using Ninject's Dependency Resolver to resolve
authService
andcacheManager
but you can skip caching and replace authService with ASP.NET IdentityUserManager
I believe.I also wanted to give credit where it's due as the WorkContext class is heavily inspired by NugetGallery project.
I bet HttpContext.Current.User is null. So instead of this:
you can try this:
I've had the same error.
My problem was that with anonymous users I wasn't setting the IIdentity on IPrincipal. I did this only when users logged in with user name. Otherwise, IIdentity was null.
My solution was to always set IIdentity. If user is not authenticated (anonymous user) then IIdentity.IsAuthenticated is set to false. Otherwise, true.
My code:
I can get something to work using
Claims
based security, so if you're looking to get something done quickly here is what I have at the moment:In the login process in the
AccountController
(mine is withinSignInAsync
method), add a new claim to the identity created byUserManager
:Then in my base controller classes I simply added a property:
This link was helpful for claims knowledge: http://msdn.microsoft.com/en-us/library/ms734687.aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1
Update for the issue with IPrincipal
I tracked it down to the
Identity
property. The issue was that I was providing a default constructor on thePatientPortalPrincipal
class that was not setting the Identity property. What I ended up doing was removing the default constructor and calling the correct constructor from withinApplication_PostAuthenticateRequest
, updated code is belowThat makes the whole thing work!