ASP.NET MVC custom IPrincipal injection - asp.net-mvc

I'm working on an application using ASP.NET MVC 1.0 and I'm trying to inject a custom IPrincipal object in to the HttpContext.Current.User object.
With a traditional WebForms application I've used the Application_AuthenticateRequest event to do this as follows.
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (HttpContext.Current.User != null)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
if (HttpContext.Current.User.Identity is FormsIdentity)
{
// Get Forms Identity From Current User
FormsIdentity id = (FormsIdentity)HttpContext.Current.User.Identity;
// Get Forms Ticket From Identity object
FormsAuthenticationTicket ticket = id.Ticket;
// Create a new Generic Principal Instance and assign to Current User
SiteUser siteUser = new SiteUser(Convert.ToInt32(id.Name));
HttpContext.Current.User = siteUser;
}
}
}
}
So using this I was able to access my custom IPrincipal by either explicitly casting the User object to type SiteUser. I actually did this by having a custom class that all Pages were inheriting from which did this under the covers for me.
Anyhow, my problem is that with ASP.NET MVC the Application_AuthenticateRequest seems to fire whenever any request is made (so for JS files, images etc.) which causes the application to die.
Any help or suggestions as to how I can go about injecting my custom IPrincipal in to the HttpContext.Current.User object within ASP.NET MVC 1.0 would be greatly appreciated. I did see the following post on SO, but it didn't seem to cater for what I'm trying to achieve: ASP.NET MVC - Set custom IIdentity or IPrincipal
TIA.

my problem is that with ASP.NET MVC
the Application_AuthenticateRequest
seems to fire whenever any request is
made (so for JS files, images etc.)
which causes the application to die.
This isn't an uniquely MVC problem - if you ran your application on IIS7 with the integrated pipeline in place then you would see the same thing.
If the problem with the lookup is scalability then I assume the actual problem is within
FormsAuthenticationTicket ticket = id.Ticket;
SiteUser siteUser = new SiteUser(Convert.ToInt32(id.Name));
I'd guess that your SiteUser class does some sort of database lookup. If you examine how forms auth works the ticket contains all the information necessary to produce a FormsIdentity (this doesn't hold true for roles, unless you specifically enable roles caching to a cookie). So you ought to look at the same approach. The first time you construct your siteUser object cache it within a signed cookie, then use the cookie to rehydrate your SiteUser properties on subsequent requests.
If you do this then you can go one step further, replacing the Thread principle with your SiteUser, or at least a custom IPrincipal/IUser combination which has the same information as your SiteUser class would have.
So inside AuthenticateRequest you'd have some flow like
SiteUserSecurityToken sessionToken = null;
if (TryReadSiteUserSecurityToken(ref sessionToken) && sessionToken != null)
{
// Call functions to attach my principal.
}
else
{
if (HttpContext.Current.User != null &&
HttpContext.Current.User.Identity.IsAuthenticated &&
HttpContext.Current.User.Identity is FormsIdentity)
{
// Get my SiteUser object
// Create SiteUserSecurityToken
// Call functions to attach my principal.
}
}
And the function to attach the principal would contain something like
HttpContext.Current.User = sessionSecurityToken.ClaimsPrincipal;
Thread.CurrentPrincipal = sessionSecurityToken.ClaimsPrincipal;
this.ContextSessionSecurityToken = sessionSecurityToken;
You'll want to make sure that the functions which write the Security Token to a cookie add, at a minimum, a checksum/MAC value, and, if you like, support encryption using the machine key if it's configured to do so. The read functions should validate these values.

This sounds like a job for a custom Authorization Filter.

Related

Failing authentication in MVC pipeline

Given that the FormsAuthentication module fires before a custom http module that handles the OnAuthenticateRequest, I'm curious if one can cancel or invalidate the forms authentication based on my own criteria.
Basically I have a process where the user logs in. After that they get a token. I get the token back after the forms authentication fires upon subsequent requests. What I want to do is then validate that the token hasn't expired against our back end server. If it's expired I need to do something so that they are forced to log back in. My thought was to do something in my OnAuthenticateRequest handler that would get picked up later in the pipeline and force a redirect back to login page or something. Is that possible?
In an ASP.NET MVC application in order to handle custom Authentication and Authorization people usually write custom Authorize attributes. They don't deal with any OnAuthenticateRequest events. That's old school. And by the way if you are going to be doing some custom token authentication why even care about Forms Authentication? Why not replace it?
So:
public class MyAuthorizeAttribute: AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
string token = GetTokenFromHttpContext(httpContext);
if (IsTokenValid(token))
{
// The user has provided a valid token => you need to set the User property
// Obviously here based on the token value you already know which is the
// associated user and potential roles, so you could do additional checks
var identity = new GenericIdentity("john.smith");
var user = new GenericPrincipal(identity, new string[0]);
httpContext.User = user;
return true;
}
// Notice that here we are never calling the base AuthorizeCore method
// but you could call it if needed
return false;
}
private string GetTokenFromHttpContext(HttpContextBase httpContext)
{
// TODO: you know what to do here: get the token from the current HTTP Context
// depending on how the client passed it in: HTTP request header, query string parameter, cookie, ...
throw new NotImplementedException();
}
private bool IsTokenValid(string token)
{
// TODO: You know what to do here: go validate the token
throw new NotImplementedException();
}
}
Now all that's left is to decorate your controllers/actions with this custom attribute instead of using the default one:
[MyAuthorize]
public ActionResult SomeAction()
{
// if you get that far you could use the this.User property
// to access the currently authenticated user
...
}
Is that possible?
This is definitely possible. You could even set your autehtication scheme to None so that forms module isn't there in the pipeline and have only your own module.
However, even if forms is there, your custom module can override the identity set for the current request. Note also that until the forms cookie is issued, forms module doesn't set the identity. This was quite common to use both forms module and the SessionAuthenticationModule - forms does the job of redirecting to the login page and the session auth module handles its own authentication cookie.
This means that you can safely mix the two: the forms module and your own custom module for a similar scenario.
Darin suggests another approach and this of course is valid too. An advantage of an authentication module (versus the authentication filter) is that the authentication module could support other ASP.NET subsystems (web forms / wcf / webapi).

Obtaining the current Principal outside of the Web tier

I have the following ntier app: MVC > Services > Repository > Domain. I am using Forms authentication. Is it safe to use Thread.CurrentPrincipal outside of my MVC layer to get the currently logged in user of my application or should I be using HttpContext.Current.User?
The reason I ask is there seems to be some issues around Thread.CurrentPrincipal, but I am cautious to add a reference to System.Web outside of my MVC layer in case I need to provide a non web font end in the future.
Update
I have been following the advice recieved so far to pass the username into the Service as part of the params to the method being called and this has lead to a refinement of my original question. I need to be able to check if the user is in a particular role in a number of my Service and Domain methods. There seems to be a couple of solutions to this, just wondering which is the best way to proceed:
Pass the whole HttpContext.Current.User as a param instead of just the username.
Call Thread.CurrentPrincipal outside of my web tier and use that. But how do I ensure it is equal to HttpContext.Current.User?
Stick to passing in the username as suggested so far and then use Roles.IsUserInRole. The problem with this approach is that it requires a ref to System.Web which I feel is not correct outside of my MVC layer.
How would you suggest I proceed?
I wouldn't do either, HttpContext.Current.User is specific to your web layer.
Why not inject the username into your service layer?
Map the relevant User details to a new Class to represent the LoggedInUser and pass that as an argument to your Business layer method
public class LoggedInUser
{
public string UserName { set;get;}
//other relevant proerties
}
Now set the values of this and pass to your BL method
var usr=new LoggedInUser();
usr.UserName="test value "; //Read from the FormsAuthentication stuff and Set
var result=YourBusinessLayerClass.SomeOperation(usr);
You should abstract your user information so that it doesn't depend on Thread.CurrentPrincipal or HttpContext.Current.User.
You could add a constructor or method parameter that accepts a user name, for example.
Here's an overly simplified example of a constructor parameter:
class YourBusinessClass
{
string _userName;
public YourBusinessClass(string userName)
{
_userName = userName;
}
public void SomeBusinessMethodThatNeedsUserName()
{
if (_userName == "sally")
{
// do something for sally
}
}
}
I prefer option number 2( use Thread.CurrentPrincipal outside of web tier ). since this will not polute your service tier & data tier methods. with bonuses: you can store your roles + additional info in the custom principal;
To make sure Thread.CurrentPrincipal in your service and data tier is the same as your web tier; you can set your HttpContext.Current.User (Context.User) in Global.asax(Application_AuthenticateRequest). Other alternative location where you can set this are added at the bottom.
sample code:
//sample synchronizing HttpContext.Current.User with Thread.CurrentPrincipal
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
//make sure principal is not set for anonymous user/unauthenticated request
if (authCookie != null && Request.IsAuthenticated)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
//your additional info stored in cookies: multiple roles, privileges, etc
string userData = authTicket.UserData;
CustomPrincipal userPrincipal = PrincipalHelper.CreatePrincipal(authTicket.Name, authTicket.UserData, Request.IsAuthenticated);
Context.User = userPrincipal;
}
}
of course first you must implement your login form to create authorization cookies containing your custom principal.
Application_AuthenticateRequest will be executed for any request to server(css files, javascript files, images files etc). To limit this functionality only to controller action, you can try setting the custom principal in ActionFilter(I haven't tried this). What I have tried is setting this functionality inside an Interceptor for Controllers(I use Castle Windsor for my Dependency Injection and Aspect Oriented Programming).
I believe you are running into this problem because you need to limit your domains responsibility further. It should not be the responsibility of your service or your document to handle authorization. That responsibility should be handled by your MVC layer, as the current user is logged in to your web app, not your domain.
If, instead of trying to look up the current user from your service, or document, you perform the check in your MVC app, you get something like this:
if(Roles.IsUserInRole("DocumentEditorRole")){
//UpdateDocument does NOT authorize the user. It does only 1 thing, update the document.
myDocumentService.UpdateDocument(currentUsername, documentToEdit);
} else {
lblPermissionDenied.InnerText = #"You do not have permission
to edit this document.";
}
which is clean, easy to read, and allows you to keep your services and domain classes free from authorization concerns. You can still map Roles.IsUserInRole("DocumentEditorRole")to your viewmodel, so the only this you are losing, is the CurrentUserCanEdit method on your Document class. But if you think of your domain model as representing real world objects, that method doesn't belong on Document anyway. You might think of it as a method on a domain User object (user.CanEditDocument(doc)), but all in all, I think you will be happier if you keep your authorization out of your domain layer.

How can I assign a value to HttpContext.User in ASP.NET MVC?

I write a controller like below:
public class AccountController : Controller
{
public ActionResult Login(/*---*/)
{
GenericIdentity identity = new GenericIdentity("userName");
GenericPrincipal principal = new GenericPrincipal(identity, new string[] { "role1", "role2" });
this.HttpContext.User = principal;
/*---*/;
}
}
After login, I can get user name by User.Identity.Name in other controller.
But User.IsInRole("role1") always return false.
How can I assign a value to User, I don't want to use Membership...
You need to persist the user data somewhere so that all subsequent page requests have access to it. Usually you would create an authentication ticket and store it in a cookie. Then for each request you extract the data and create your IPrincipal. This can be done in the Application_AuthenticateRequest method of Global.ascx,
MVC - How to store/assign roles of authenticated users has more information on a simple way to do what you want.
Hm.
Using membership?
At least the lower level API. You need to assign it a principal in some event (which basically turns into a cookie and is deserialized with every call).
Details are in http://support.microsoft.com/kb/306590
Or also in http://msdn.microsoft.com/en-us/library/aa302399.aspx

How does the Authorize tag work? - ASP.NET MVC

How does the Authorize Tag determine if the user is authorized or not?
Like say, if a user logs in and they try to go to a view that has an Authorize tag. How does it determine if a user is authorized or not? Does it do a query to database and check?
How about if they go to a view with a role authorization? Does it query the membership role table?
I am just wondering since I have what the ASP.NET membership tables considers duplicate userNames. I use a serious of fields to determine which user is what, allowing users to have the same duplicate userName, but still be unique in my database.
This caused me to have to write custom methods for lots of .NET membership stuff since it all used "userName" to do searching instead of using the UserId.
So I am now wondering if this could be the case with the Authorize tag. Since I have no clue how it works and like if I was not using .NET membership I would not have a clue how it would determine it.
The Authorize tag uses all the built in membership checks from ASP.NET. It's VERY easy to roll your own tag. For example:
public class MyAuthorize : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null) throw new ArgumentNullException("httpContext");
// Make sure the user is authenticated.
if (httpContext.User.Identity.IsAuthenticated == false) return false;
// Do you own custom stuff here
bool allow = CheckIfAllowedToAccessStuff();
return allow;
}
}
You then can use the [MyAuthorize] tag which will use your custom checks.
ControllerActionInvoker parses the attribute and calls OnAuthorization() on it when it's time to check the credentials.
The AuthorizationAttribute.OnAuthorization() method basically checks to see if User.Identity.IsAuthenticated is true or not. This just draws on the functionality of FormsAuthentication or whatever other authentication scheme you may be using.

asp.net mvc authentication against shibboleth and authorization

Where do I get information about the currently connected user? That is, how does shibboleth pass the information?
Can I set some restrictions on actions using [Authorize] attribute based on data acquired from shibboleth?
Shibboleth publishes user attributes associated with
sessions into HTTP request headers, based on header names defined
in Attribute Acceptance Policy (1.3.x) or Attribute Mapping (2.x)
files. These headers are transformed into CGI variables based
on mapping rules defined by the CGI specification.
You should be aware of this security advisory:
http://shibboleth.net/community/advisories/secadv_20090615.txt
I have never user shibboleth, but you can get information about the user from Controller.User property. It will return a generic principal of current thread. Using this principal you can check whether the user is authenticated and get a login name of the user. This is due to the reason that after logon an authentication cookie is set and this cookie contains limited amount of information. And on each request after logon only this cookie is checked (if it exists and valid - user is authenticated).
So if you need in some specific information you can manually load a user (it's better to use cache here) and check whatever you want.
Also you can create and attach your own principal with necessary information to the thread on start of a request (e.g. on start of a request load the user from db/cache using user name from base principal, create and set your own principal to thread). After this you can check all properties of the user you need.
Where would you attach your own principal? You say on the start of the request but what if you don't want every request authorizing?
You'll want to create a method in Global.asax.cs that has the following signature
protected void Application_PostAuthenticateRequest()
{
//Your code here.
}
This will be called automatically before almost anything else is done (MVC will call this method if it exists, you don't have to "turn it on" anywhere), and this is where you need to set the Principal. For instance, let's assume you have a header called RolesHeader that has a comma separated value of roles and another header called UserId that has (duh) the user ID.
Your code, without any error handling, might look something like:
protected void Application_PostAuthenticateRequest()
{
var rolesheader = Context.Request.Headers["RolesHeader"];
var userId = Context.Request.Headers["UserId"];
var roles = rolesheader.Split(',');
var principal = new GenericPrincipal(new GenericIdentity(userId), roles);
Context.User = principal;
}
It's the Principal/Identity that the [Authorize] attribute uses, so setting it here at the beginning of the request lifecycle means the [Authorize] attribute will work correctly.
The rest of this is optional, but I recommend it:
I like to create my own custom classes that implement IPrincipal and IIdentity instead of using the GenericPrincipal and GenericIdentity, so I can stuff more user information in it. My custom Principal and Identity objects then have much more rich information, such as branch numbers or email addresses or whatever.
Then, I create a Controller called BaseController that has the following
protected new CustomPrincipal User
{
get
{
return (base.User as CustomPrincipal) ?? CustomPrincipal.GetUnauthorizedPrincipal();
}
}
This allows me to access all my rich, custom Principal data instead of just what's defined in IPrincipal. All of my real controllers then inherit from BaseController instead of directly from Controller.
Obviously, when using a custom Principal like this, in the Application_PostAuthenticateRequest() method, you'd set the Context.User to be your CustomPrincipal instead of a GenericPrincipal.

Resources