views:

104

answers:

1

I have what seems like a common issue with SaaS applications, but have not seen this question on here anywhere.

I am using ASP.NET MVC with Forms Authentication. I have implemented a custom membership provider to handle logic, but have one issue (perhaps the issue is in my mental picture of the system).

As with many SaaS apps, customers create accounts and use the app in a way that looks like they are the only ones present (they only see their items, users, etc.). In reality, there are generic controllers and views presenting data depending on the customer represented in the URL. When calling something like the MembershipProvider.ValidateUser, I have access to the user's customer affiliation in the User object - what I don't have is the context of the request to compare whether it is a data request for the same customer as the user.

As an example,

One company called ABC goes to abc.mysite.com

Another company called XYZ goes to xyz.mysite.com

When an ABC user calls

http://abc.mysite.com/product/edit/12 

I have an [Authorize] attribute on the Edit method in the ProductController to make sure he is signed in and has sufficient permission to do so.

If that same ABC user tried to access

http://xyz.mysite.com/product/edit/12 

I would not want to validate him in the context of that call. In the ValidateUser of the MembershipProvider, I have the information about the user, but not about the request. I can tell that the user is from ABC, but I cannot tell that the request is for XYZ at that point in the code.

How should I resolve this?

+1  A: 

Because Authorize is on the same thread as the request you could determine the subdomain by inspecting:

System.Web.HttpContext.Current.Request.Url.DnsSafeHost

Doing so on each call would certainly keep things in order, however this is a purely cosmetic check during authorization. I recommend that you simply look at this information during authentication. Once you know they are requesting XYZ and authenticate them into it, authorization should be only concerned about controlling features/data they have access to as XYZ. Their being from XYZ should be stored as part of the CurrentUser from that point.

Nissan Fan
Wouldn't that mean that an ABC user could authenticate to ABC, then paste an XYZ URL in the browser and continue, or is there another mechanism stopping them?
Jim Blake
No. Remember that domains are just names by themselves that resolve to numbers. It's an abstraction of an IP address. You only care about the subdomain xyz when you authenticate the user. Once you know that user Bob came in on xyz.domain.com, you try his password against XYZ. If he authenticates then you store his user context as pointing to XYZ company. Going forward, it doesn't matter if he changes the subdomain to abc because you know he's Bob from XYZ. He could change it to anything, but from that point on you've authenticated him out as Bob/XYZ.
Nissan Fan
Subdomains, unless you load different apps on different subs, are just abstractions of the same IP address. I assume you have one web app and it resolves out the subdomain as part of authentication?
Nissan Fan
Lastly, you could check the subdommain on every request, but it's simply pointless since no matter what you will only represent him as Bob @ XYZ.
Nissan Fan
@Nissan Fan - correct - one app - I am just using the subdomain to resolve in the routing as an additional parameter to the controllers. Whether the text of the subdomain or the CustomerID that corresponds to it, I still would want to test the user against the request he made. I suppose that could be done in the model logic by joining the user on every request for data.
Jim Blake