Custom IPrincipal with Forms Authentication in ASP.NET MVC
Asked Answered
B

1

13

This should be simple, but I simply cannot figure it out after all my googling. Here's what I want. I have a custom Users table (no roles at the moment) that I'd like to authorize against. For this I'll have to implement a custom Membership Provider which I've already done. My question is though, how do I go about setting a custom IPrincipal to the HttpContext.Current.User? Where should that happen? Let's say I have the following setup:

public class CustomMembershipProvider : MembershipProvider
{
   private IUserRepository _repository;

   public CustomMembershipProvider(IUserRepository repository)
   {
      _repository = repository;
   }

   public override bool ValidateUser(string username, string password)
   {
      //check user repository 
   }

   //additional methods omitted for brevity 
}

I then have a custom user class the implements IPrincipal and IIdentity

 public class CustomUser : IPrincipal
    {
        public CustomUser(string name, int userId, string additionalInfo)
        {
            Identity = new CustomIdentity(name, userId, additionalInfo);
        }

        public IIdentity Identity { get; private set; }

        public bool IsInRole(string role)
        {
            return true;
        }
    }

    public class CustomIdentity : IIdentity
    {
        public CustomIdentity(string name, int userId, string additionalInfo)
        {
            Name = name;
            UserId = userId;
            AdditionalInfo = additionalInfo;
        }

        public string Name { get; private set; }

        public string AuthenticationType
        {
            get { return "CustomAuth"; }
        }

        public bool IsAuthenticated
        {
            get { return !String.IsNullOrEmpty(Name); }
        }

        public int UserId { get; private set; }
        public string AdditionalInfo { get; private set; }
    }

So my question is, where is the correct place to set the Context.User to an instance of this custom user? Do I need a custom Authorize Attribute? If so, what would that look like?

Burgundy answered 21/8, 2011 at 18:12 Comment(2)
How did you get on with this in the end? I'm in the early days of doing something similar to this myself.Supercilious
I'm on the same boat, i'm almost ready to create an extension method to do the job!Dodona
P
2

I suggest using a custom controller base class for all your controllers

Then, in OnAuthorization, call

protected override void OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext)
{
    // Actual Authentication
    HttpContext.User = user; 
    Thread.CurrentPrincipal = user;
    base.OnAuthorization(filterContext);
}

I'm not entirely sure how to invoke the membership provider because I'm doing it manuallt, but I think you have static access to Membership.Provider to perform the actual object construction.

Do I need a custom Authorize Attribute?

No. Notice the difference of authentication and authorization: authentication establishes the user's identity in your system. Authorization allows or denies a specific request. Hence, AuthorizeAttribute also has a Roles parameter to allow an action to be called only by certain users.

Pudens answered 21/8, 2011 at 18:17 Comment(5)
I'm not sure I understand your answer. When you say Actual Authentication, are you talking about actually setting the AuthCookie there? What then would the Loing form itself do on post?Burgundy
The way I understand the MembershipProvider, ValidateUser shouldn't be called very often - as soon as the user logs in, he recvs an authentication cookie which will be used henceforth. However, to get the user object you'll have to hit the database (or some cache). But that's a requirement of your code. I think the MembershipProvider is not very well designed, doing it manually has a lot of benefits.Pudens
Now you changed your comment while I was writing mine :) OnAuthorization will be called for every request, even if the user is already (forms-)authenticated; You're hitting the DB only to fetch the user object.Pudens
Sorry about that :) Hitting the DB on every request sounds less than ideal. More than that though, I'm still unclear as to what the //Actual Authentication part should look like. In my Login form I'd call MembershipProvider.Validate() (or something custom as you suggest), but then what should happen for every request in the OnAuthorization?Burgundy
OnAuthorization merely fetches the user object from the database so you have your user set up. You probably need the user in every request anyway -- for example, to make sure the user is allowed that particular request at this time. Caching is a good idea, of course. Then you can safely access HttpContext.User at all times. The actual authentication (I didn't write that) is what happens when the form is submitted and FormsAuthentication.SetAuthCookie is called.Pudens

© 2022 - 2024 — McMap. All rights reserved.