I need to do something fairly simple: in my ASP.NET MVC application, I want to set a custom IIdentity / IPrincipal. Whichever is easier / more suitable. I want to extend the default so that I can call something like User.Identity.Id
and User.Identity.Role
. Nothing fancy, just some extra properties.
I've read tons of articles and questions but I feel like I'm making it harder than it actually is. I thought it would be easy. If a user logs on, I want to set a custom IIdentity. So I thought, I will implement Application_PostAuthenticateRequest
in my global.asax. However, that is called on every request, and I don't want to do a call to the database on every request which would request all the data from the database and put in a custom IPrincipal object. That also seems very unnecessary, slow, and in the wrong place (doing database calls there) but I could be wrong. Or where else would that data come from?
So I thought, whenever a user logs in, I can add some necessary variables in my session, which I add to the custom IIdentity in the Application_PostAuthenticateRequest
event handler. However, my Context.Session
is null
there, so that is also not the way to go.
I've been working on this for a day now and I feel I'm missing something. This shouldn't be too hard to do, right? I'm also a bit confused by all the (semi)related stuff that comes with this. MembershipProvider
, MembershipUser
, RoleProvider
, ProfileProvider
, IPrincipal
, IIdentity
, FormsAuthentication
.... Am I the only one who finds all this very confusing?
If someone could tell me a simple, elegant, and efficient solution to store some extra data on a IIdentity without all the extra fuzz.. that would be great! I know there are similar questions on SO but if the answer I need is in there, I must've overlooked.
Here is an example to get the job done. bool isValid is set by looking at some data store (lets say your user data base). UserID is just an ID i am maintaining. You can add aditional information like email address to user data.
in the golbal asax add the following code to retrive your information
When you are going to use the information later, you can access your custom principal as follows.
this will allow you to access custom user information.
Here is a solution if you need to hook up some methods to @User for use in your views. No solution for any serious membership customization, but if the original question was needed for views alone then this perhaps would be enough. The below was used for checking a variable returned from a authorizefilter, used to verify if some links wehere to be presented or not(not for any kind of authorization logic or access granting).
Then just add a reference in the areas web.config, and call it like below in the view.
Based on LukeP's answer, and add some methods to setup
timeout
andrequireSSL
cooperated withWeb.config
.The references links
Modified Codes of LukeP
1, Set
timeout
based onWeb.Config
. The FormsAuthentication.Timeout will get the timeout value, which is defined in web.config. I wrapped the followings to be a function, which return aticket
back.2, Configure the cookie to be secure or not, based on the
RequireSSL
configuration.Here's how I do it.
I decided to use IPrincipal instead of IIdentity because it means I don't have to implement both IIdentity and IPrincipal.
Create the interface
CustomPrincipal
CustomPrincipalSerializeModel - for serializing custom information into userdata field in FormsAuthenticationTicket object.
LogIn method - setting up a cookie with custom information
Global.asax.cs - Reading cookie and replacing HttpContext.User object, this is done by overriding PostAuthenticateRequest
Access in Razor views
and in code:
I think the code is self-explanatory. If it isn't, let me know.
Additionally to make the access even easier you can create a base controller and override the returned User object (HttpContext.User):
and then, for each controller:
which will allow you to access custom fields in code like this:
But this will not work inside views. For that you would need to create a custom WebViewPage implementation:
Make it a default page type in Views/web.config:
and in views, you can access it like this:
MVC provides you with the OnAuthorize method that hangs from your controller classes. Or, you could use a custom action filter to perform authorization. MVC makes it pretty easy to do. I posted a blog post about this here. http://www.bradygaster.com/post/custom-authentication-with-mvc-3.0
As an addition to LukeP code for Web Forms users (not MVC) if you want to simplify the access in the code behind of your pages, just add the code below to a base page and derive the base page in all your pages:
So in your code behind you can simply access:
What I'm missing in a Web Form scenario, is how to obtain the same behaviour in code not tied to the page, for example in httpmodules should I always add a cast in each class or is there a smarter way to obtain this?
Thanks for your answers and thank to LukeP since I used your examples as a base for my custom user (which now has
User.Roles
,User.Tasks
,User.HasPath(int)
,User.Settings.Timeout
and many other nice things)