using custom IPrincipal and IIdentity in MVC3

agent47 picture agent47 · May 24, 2012 · Viewed 12.3k times · Source

I create my own IPrincipal and IIdentity implementation as shown below:

[ComVisible(true)]
[Serializable]
public sealed class CustomIdentity : IIdentity {

    private readonly string _name;
    private readonly string _email;
    // and other stuffs

    public CustomIdentity(string name) {
        _name = name.Trim();
        if(string.IsNullOrWhiteSpace(name))
            return;
        _email = (connect to database and read email and other stuffs);
    }

    public string Name {
        get { return _name; }
    }

    public string Email {
        get { return _email; }
    }

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

    public bool IsAuthenticated {
        get { return !string.IsNullOrWhiteSpace(_name); }
    }

}


[ComVisible(true)]
[Serializable]
public sealed class CustomPrincipal : IPrincipal {

    private readonly CustomIdentity _identity;

    public CustomPrincipal(CustomIdentity identity) {
        _identity = identity;
    }

    public bool IsInRole(string role) {
        return _identity != null && 
               _identity.IsAuthenticated &&
               !string.IsNullOrWhiteSpace(role) &&
               Roles.IsUserInRole(_identity.Name, role);
    }

    IIdentity IPrincipal.Identity {
        get { return _identity; }
    }

    public CustomIdentity Identity {
        get { return _identity; }
    }

}

Also, I create a HttpModule and in its AuthenticateRequest event, I do this:

    public void Init(HttpApplication context) {
        _application = context;
        _application.AuthenticateRequest += ApplicationAuthenticateRequest;
    }

    private void ApplicationAuthenticateRequest(object sender, EventArgs e) {
        var formsCookie = _application.Request.Cookies[FormsAuthentication.FormsCookieName];
        var identity = formsCookie != null
             ? new CustomIdentity(FormsAuthentication.Decrypt(formsCookie.Value).Name)
             : new CustomIdentity(string.Empty);
        var principal = new CustomPrincipal(identity);
        _application.Context.User = Thread.CurrentPrincipal = principal;
    }

Also, I create my own Controller and WebViewPage like these:

public abstract class CustomController : Controller {
    public new CustomPrincipal User {
        get {
            var user = System.Web.HttpContext.Current.User as CustomPrincipal;
            return user;
        }
    }
}


public abstract class CustomWebViewPage<TModel> : WebViewPage<TModel> {
    public new CustomPrincipal User {
        get {
            // (Place number 1) here is the error I'm speaking about!!!
            var user = HttpContext.Current.User as CustomPrincipal;
            return user;
        }
    }
}

as shown in above code, it seems everything is right; But as you can see, in Place number 1 I can't access the CustomPrincipal! Means in this place, I have a RolePrincipal instead of having a CustomPrincipal. e.g. HttpContext.Current.User is a RolePrincipal instead of CustomPrincipal. But the RolePrincipal.Identity property is a CustomIdentity!

Answer

ravy amiry picture ravy amiry · May 24, 2012

Your mistake is here:

_application.AuthenticateRequest += ApplicationAuthenticateRequest;

There is a HttpModule named RoleManagerModule that invokes a method in HttpApplication.PostAuthenticateRequest and sets the HttpContext.Current.User to RolePrincipal. So, you were setting the User in AuthenticateRequest and the RoleManagerModule sets it in PostAuthenticateRequest, means after your set, so overrides your settings. Change your Module.Init:

public void Init(HttpApplication context) {
    _application = context;
    // change just this line:
    _application.PostAuthenticateRequest += ApplicationAuthenticateRequest;
}

IMPORTANT UPDATE:

Please see this question -asked by starter again, depended on current question- for a second solution, if this one doesn't work.