ASP.NET Multi tenant application with tenant specific roles - c#

We have a multi-tenant ASP.NET application. So far tenants have been isolated from each other, however now we have Agencies that manage multiple tenants and want to be able to manage all their tenants with a single user account. I'm trying to figure out the best way to accomplish that, hopefully without much change to the existing technologies that we are using.
Relevant technical details:
AspNetSqlMembershipProvider for both membership and roles
C# 4.0 (soon to be 4.5)
Forms Authentication
Both aspx and MVC (v3) pages
Assume 100 or more tenants, so any solution needs to support that
I believe the requirements are very similar to the security model for SQL Server. We have one set of logins which represent all the users that can login to the system. Users should be able to be given roles to one or more databases (tenants). Example: User Bob has admin role in company A, but only user role in company B. We also have a "sysadmin" role for my company's employees which allow us access to any tenant as well as specialized administrative privileges such as create/delete tenants, etc.
I've done a lot of research into various libraries, frameworks, etc, and I haven't found any convincing evidence that some other library or framework will be better than what we currently have. So I'm currently thinking of just figuring out how to make Sql Membership provider do what I want, unless someone can point me in a better direction. I'm also not sure I know the best terms to search for in this.
I've got 2 options I'm considering:
Add only a handful of roles to the membership provider and handle all the questions of "does the current user have this role in this tenant" outside of membership provider. Membership provider would be used to handle basic access to the system.
Add tenant specific roles to membership provider. We would have (# of roles) x (# of tenants) total roles in the system. Each new tenant would add another set of roles to the system, e.g. "Tenant A:Admin", "Tenant A:User", etc. Would need some additional tables to manage the relations as well as probably some custom code to ensure that access is requesting the correct tenant-specific role from the membership provider.
Are either of these options good? Or should I be looking elsewhere for support for this?

I don't think you are going to be able to shoehorn multitenancy into any out of the box role provider, so you might as well keep using SqlMembershipProvider (and SqlRoleProvider). Even the newest Microsoft.AspNet.Identity still assumes a vanilla many-to-many between users and roles. What you really need is to add a 3rd column to the primary key of that many-to-many table, which will id your tenant, i.e.:
user: 6
role: 4
tenant: 17
user: 6
role: 9
tenant: 18 (and so on)
...with this, you are able to have users with different privileges for different tenancies, all using the same set of role names.
If you went with option #2, then your [Authorize] attributes would explode. Imagine this:
[Authorize(Roles = "TenantA:Admin", "TenantB:Admin", ...)]
public ActionResult Post(int id, SomeViewModel model) {}
... all of those attributes would have to be written at compile time unless you went with a custom AuthorizeAttribute, which you could do. But even then you are left creating a new set of roles each time you add a tenant to the system, which should not be necessary.

I work on a big multi-tenancy application. We came to the conclusion that it was easier to maintain separate databases per tenant, and have the web application automatically switch database contexts, rather than try and use an over-complicated database schema to model different tenants.
The benefits
Tenant data is compartmentalized by default into different databases
Tenant data can be exported as a database dump for client MI
Database design is vastly simplified
The drawbacks
You have to manage multiple databases - operations challenge
You have to develop database switching code
Implementation using multiple databases
We used a configuration database that has client settings based on an account code. That account code can come from a login screen or you can map subdomain to client code.
When the app starts you load all tenants into cache (containing connection strings)
On every request you have to determine the client and then switch the db context
I have also developed a multi-tenant application that uses a single database. You quite quickly have problems making sure that you don't cross tenant data. Every query needs to include a tenant id filter. The database queries are therefore always slower as a result, although you can index everything you can to try and improve the situation.
With regards to the Membership question, you can install the membership schema into each tenant database.
What doesn't work
The ideal alternative would be to dynamically switch the ApplicationName, but although it seems to work, ApplicationName is not thread safe, therefore this would not be reliable:
Because a single default membership provider instance is used for all
of the requests served by an HttpApplication object, you can have
multiple requests executing concurrently and attempting to set the
ApplicationName property value. The ApplicationName property is not
thread safe for multiple writes, and changing the ApplicationName
property value can result in unexpected behavior for multiple users of
an application. We recommend that you avoid writing code that allows
users to set the ApplicationName property, unless you must. An example
of an application where setting the ApplicationName property may be
required is an administrative application that manages membership data
for multiple applications. Such an application should be a single-user
application and not a Web application.
Alternative: MembershipReboot
Multi-tenancy is hard in .Net. An open source alternative to using the built in Membership is to use MembershipReboot, written by Brock Allen. It has some excellent features including multi-tenant support out-of-the-box:
single- or multi-tenant account management
flexible account storage design (relational/SQL or object/NoSql), samples using both EF and RavenDB
claims-aware user identities
support for account registration, email verification, password reset, etc.
account lockout for multiple failed login attempts (password guessing)
extensible templating for email notifications
customizable username, password and email validation
notification system for account activity and updates (e.g. for auditing)
account linking with external identity providers (enterprise or social)
supports certificate based authentication
proper password storage (via PBKDF2)
configurable iterations
defaults to OWASP recommendations for iterations (e.g. 64K in year 2012)
two factor authentication support via mobile phone SMS messages or client certificates
The most common use case will be to integrate this into an ASP.NET or
ASP.NET MVC application, though the library can also be used over a
network as a service.
Alternative: ServiceStack REST
Another alternative if you are building modern web applications that heavily use JavaScript MVC frameworks such as AngularJS, EmberJS or BackboneJS is to use ServiceStack REST services. ServiceStack has a long list of Authentication features, and from my experience of SS, I find it has an extremely well thought out API model.

Related

How to organize communication in an asp.net web application using separate authorization server, web api, ui?

The requirements
we have 3 modules ui, api, IdentityServer (IS) (client, resource, IS in terms of IdentityServer)
all the modules should be separated from each other (separate dbs for IS and api)
api is stateless (all the needed auth info got from tokens)
the api will have resources like \projects, \users, etc.
another entry point may be added in the future like another-ui which will communicate with the IS and api and will have its own claims
The problems
The main problem is that the resources of api like \projects\12345, \users\, \projects\123456\users, etc. may also be needed as claims in IS. For example, api module reads the access token of authorized user and see the claim projects that equals ["222", "12345"], so the resource \projects\12345 or \projects\123456\users are allowed for that user.
Users are identities in IS and resources in api at the same time. Projects are claims in IS and resources in api at the same time.
I thought of book-keeping these entities that are represented in both modules through the ids (guids). But ids won`t solve all the problems.
Some of them are:
creation of a new project with its id should grant that user the rights to use it in the future, so we need save the claim for that user in some way. The modules are separated, so should we call the IS api to create that claim for that user and then proceed with project creation. How the communication between the two (IS and api) should be organized? Do we need to register the api as another client in IS?
How should updates of users in IS like changing the email, phone (the values one may log in with) will update the api. I thought of showing warnings that the auth email (got from token) does not match the info email.
Could you, please, explain how modern systems coupe with the per resource access?
Thank you for your time.
First you need to make sure what a claim is.
Claim is not a permission or a role, it's what the user is. Based on what the user is, then you can assume the permissions.
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/claims?view=aspnetcore-3.0
A claim is a name value pair that represents what the subject is, not what the subject can do.
So starting from that, you can get the claims and do the following.
Let's say that a user is the owner of a project. When the new project is created, the project api can update the identity server and add a claim to the user saying he is the owner.
In your apis the owner of a project has a set of permissions and based in those, access to specific resources
In the DDD Domain driven design world, a little bit of data duplication does not matter. So duplicating the possible claims that your application needs in terms of roles (again, not ids but a mapping of one or more claims to specific roles) is not a bad practice.
When you update some kind of claim from your api, you should do so in a transactional way. Think first if you need the email to be saved in both. You will get the user data from the claims anyway on every request. Is it even something you need as a claim? If not have it in your api only.
Communication between apis is organized in many ways. If you need transactions or eventual consistency is something you should also consider. Communicating with events or queues is the microservices way to go, with patterns like the SAGA being the coordinator.

ASP.net Core Identity Complex Claims authentication

I have to create an Authentication and Authorization environment for my ASP.NET CORE MVC Identity 2.1 application, with an API backend and IdentityServer4 as Authorization Server).
I have administrators
I have Projects.
Every user has to be in at least one project, except administrators.
I would give users a claim like "Role" and store something like "admin"/"user" in it.
Now it gets more complicated.
In every project, users have different roles, and sub roles and once again sub-roles of sub-roles (so, three tiers).
Currently, I solved the problem by using action filters and write them in front of each controller/method (where I see them fit). I also created tables for these three tiers, so that I can store which user is in which tier (and of course they have different access rights depending on that).
I also want the user to be able to have multiple projects open at the same time (in different browser tabs) so that they can switch between these as they desire. I currently solved this by sending the project id on every request in the URI.
I thought about using claims instead of my current action filter solution. I read a lot into that and watched the Microsoft Virtual Academy videos regarding this topic. But I fail to understand how I could use that in this scenario. Should I do claim requirements and copy the code from my action filters in them because claims identity can't handle my case by default?
Am I doing this completely wrong?

Options for single sign-on between ASP.NET and MVC.NET solutions?

I have an ASP.NET solution that acts as the primary customer portal for my customers. On this website the users can log-in access their important financial information and more. The website uses a custom authentication scheme that checks the user's username (their email) and their password (salt-hashed) against a Users table in a local database.
I am building a new MVC.NET solution that is more of a web-app tool to be used by these same customers for ordering. I want to re-use the sign-on mechanism of the ASP.NET portal to authenticate users. The goal is to save the user from remembering two log-ins or even having to supply the same log-in twice.
What are my options for allowing users who sign on to the ASP.NET solution to then be auto authenticated to the MVC.NET solution? I've listed some ideas below but are these "bad" or is there a more elegant solution? I'd love your input.
Common Cookie I could create a common cookie that the ASP.NET site creates and the MVC.NET site looks for. But is that secure enough?
Token in Query String I could create a token id on the ASP.NET site that is stored in the local database and is then passed in the query string of the link to the MVC.NET site which takes the token id and validates it against the same database.
Hybrid A bit of both?
Other? Got a better idea?
I've recently done something quite similar (the major difference being that it was internal to the company rather than for external customers) using OpenId.
The implementation of OpenId for .NET is called DotNetOpenAuth which should be suitable for your purposes.
It did take me a while to implement; but it works very well, is very flexible, and extremely secure.
More information about openid (from Wikipedia):
OpenID is an open standard that allows users to be authenticated by certain co-operating sites (known as Relying Parties or RP) using a third party service, eliminating the need for webmasters to provide their own ad hoc systems and allowing users to consolidate their digital identities.
Users may create accounts with their preferred OpenID identity providers, and then use those accounts as the basis for signing on to any website which accepts OpenID authentication. The OpenID standard provides a framework for the communication that must take place between the identity provider and the OpenID acceptor (the "relying party").2 An extension to the standard (the OpenID Attribute Exchange) facilitates the transfer of user attributes, such as name and gender, from the OpenID identity provider to the relying party (each relying party may request a different set of attributes, depending on its requirements).
The OpenID protocol does not rely on a central authority to authenticate a user's identity. Moreover, neither services nor the OpenID standard may mandate a specific means by which to authenticate users, allowing for approaches ranging from the common (such as passwords) to the novel (such as smart cards or biometrics).
Oh, and if you'd like further encouragement, Stack Exchange uses it!
#Jmrnet: in response to your last comment:
Perhaps I was unclear. OpenId in and of itself is simply for validating credentials from one location to another (more or less). It's entirely possible to implement as an SSO model where users do nothing different whatsoever - they don't have to choose a provider, or register, or anything like that. For example, in my setup, the user enters a username and password in a web portal, and then clicks a button to launch another site being automatically logged in by OpenId. Nothing different for the user at all! OpenId can be used with any initial authentication model you can think of (note the bolded section in the snippet from wikipedia).
Take a look at SAML:
http://en.wikipedia.org/wiki/Security_Assertion_Markup_Language
It works using XML and supports encryption.
I am currently implementing two SSO solutions for the same project.
In one, we are interfacing with an external partner and are using SAML.
In the other, we are allowing logged in users access to our Sharepoint and using the "Token in Query String" approach, since we trust Sharepoint to access our membership tables. This approach is much easier than dealing with SAML tokens.
There are many methods you can use, Mansfied described OpenID and RandomUs1r described SAML. Also, you can store relevant information in localStorage or in the session. I believe you should store relevant information with session.
It is not safe to put this in the query string, because if I register and log in, I will see something like UserID=1234 in the URL. If I change that to UserID=1235 and the ID is existent, then I can do some things in the name of the other user. This is called identity theft, which should be prevented by any means possible. So you should never have this kind of info in your URLs. Also, if you store the id of the user, you should obfuscate it somehow. For instance if you store the value in local storage and instead of 1234 you store encrypt(1234, salt), then the consistency of user action will be maintained.

How can I provide an ASP.NET Forms Authentication UX while using Active Directory Role and Authentication providers?

Is it possible to use this Role Provider AspNetWindowsTokenRoleProvider with ASP.NET FORMS Authentication (via this MembershipProvider System.Web.Security.ActiveDirectoryMembershipProvider)?
It seems to only work with <authentication mode="Windows">, is it possible to use it with FORMS?
background -- The objective here is to provide an ASP.NET Forms UX while using Active Directory as the back-end authentication system. If there is another, easy way to do this using built-in technologies, that's great and I'd like to hear about that as well.
update
I should say that I have the authentication working, what I'm struggling with is adding a level of granular control (such as Roles).
Currently, I have to setup my Active Directory Connection to point to a specific OU in my domain, which limits access to only users physically in that OU -- what I'd like is to point my Active Directory connection to my entire domain, and restrict access based on Group Membership (aka Roles) this works if I use Windows Authentication -- but I'd like to have the best of both worlds, is this possible without writing my own RoleProvider?
As others have mentioned, you cannot use the ActiveDirectoryMembershipProvider with the AspNetWindowsTokenRoleProvider. If you want to use the ADMP with Forms Authentication, you have a few choices:
Use the AuthorizationManager aka AzMan. - AzMan is built into Windows 2003+ and can interact with Active Directory groups. In addition, there is an AuthorizationStoreRoleProvider built into .NET 2.0+ that you can use to interact with it. AzMan works on Task, Operations and Roles wherein presumably your application would be coded to act on specific Tasks which could then be grouped into Operations and you can then create Roles which have authority to perform various Operations. There is a management application that gets installed when you install AzMan that you can use to manage Tasks, Operations and Roles. However, there are some downsides to AzMan. First, the AuthorizationStoreRoleProvider does not recognize Tasks. Rather, it loads the Roles list with a list of Operations. Thus, unless you create a custom version of the provider, your applications would need to seek Operation names instead of Task names. Second, it can be a bear to work with in that interaction, at the lowest level, is still via COM. Unless you want your administrators having to use the AzMan tool, you'll need to write your own pages to manage Operations, Roles and membership in roles.
Use the SqlRoleProvider and map roles to usernames. The advantage of this solution is that it is very simple to implement. You can pretty much use it out of the box since the RoleProvider operates on username and not UserId. In your code you would simply check for IsInRole to determine if the given user had been dropped into a role that your code recognizes. The significant downside is that it is geared on usernames only and not AD groups and thus there is no means for an admin to use the AD tools to manage users. Instead, you have to write a management console to manage role membership. In addition, changing a username at the AD level would require an update to your application's list of known usernames.
Write (or locate) a custom AD RoleProvider that honors AD groups. Writing a custom role provider is not for the faint of heart but doing so lets administrators manage role membership using their existing AD tools.
Implement your own ADAuthorizeAttribute by inheriting from AuthorizeAttribute and overriding AuthorizeCore. It's a lot easier than implementing your own role provider or installing and configuring AzMan.
See my example here:
ASP .NET MVC Forms authorization with Active Directory groups.
Yes, you are right; it will work only with windows forms authentication. You can confirm it here:
http://msdn.microsoft.com/en-us/library/system.web.security.windowstokenroleprovider.aspx
Just a suggestion. Try AuthorizationStoreRoleProvider with AzMan (Authorization Manager). It worked for me (ages ago, so I don't remember much).

How to allow multiple authentication methods in ASP.NET?

I'm building a new ASP.NET MVC application (in C#) and one of the requirements is to create a new database of members. For this, we'd need roles to manage the different types of members and profiles to manage the additional metadata attached to each member. So far so good, just use the standard MembershipProvider, RoleProvider and ProfileProvider provided as part of the .NET Framework.
However, the catch is that I'd like to allow different authentication methods. I'd like Accounts and Login Credentials to have a one-to-many relationship (one account can have a number of login credentials attached). A user for example, might have both an OpenID and ActiveDirectory account attached to their account.
However, after experimenting with a few ways we opted for the MembershipProvider route (explained how it was achieved as an answer below).
My question is, how have people done this before and how would people suggest I approach it? It appears to be something that is achieved on quite a number of sites, yet a search on here doesn't return anything solid to play around with.
EDIT: After looking around for a good period of hours overnight and this morning - I'm still not convincinced that butchering a single MembershipProvider would have been the easiest option. Does having multiple MembershipProviders give the same effect?
BOUNTY EDIT: With no responses, I am assuming that there is no more optimal solution that the one I posted as an answer. Is this really the case? I'm offering a bounty to try and see if anyone has any further thoughts on this and whether there are better alternatives.
BOUNTY ACCEPT EDIT: I think that WIF is the answer as accepted below, for a .NET 4 release and maybe other versions as it probably works with 3.5. Other than that, maybe a butchered MembershipProvider or adapted one may still be relevant.
In my opinion, the "real way" of doing this is to use federation with WIF (Windows Identity Foundation, formerly Geneva framework).
The idea is that you separate authentication from authorization. The authentication is performed by a so-called STS (Security Token Service) and it manages all the possible login mechanism that you want to support. When a user has been authenticated the STS issues a token containing a set of claims and the user's identity.
This token is sent to the web site (called a relying party in this lingo), and the website determines which parts of the site the user has access to based on the claims in the token. WIF supplies both membership and role providers that extract information from token.
You can read about creating a claims aware website here.
One of the pros of this approach is the separation of concerns between authentication and authorization. You do not need any complex membership and roleproviders in your website. Furthermore the STS can be reused to authenticate users to other applications you might have without them having to register more than once (effectively achieving single sign-on)
The downside is that you will have to spend some time studying these concepts and coding your STS. Mind you, it is not hard to code an STS with WIF, but it is not a 100% trivial task either.
If I have managed to tickle your interest I would recommend that you start out by reading this whitepaper.
Kind regards,
Klaus
One idea we've followed is to create a custom Membership / Role / Profile provider. We customised the login / authentication methods significantly and have an additional table of logins. This table basically just contained:
LoginID (Auto-Incremental ID, PK)
UserID (FK)
LoginSystemID (FK)
...blah blah
Within the above, the LoginSystemID was a link to a foreign lookup table which helped the system to determine which authentication service to use (e.g. Standard, AD, OpenID, FacebookConnect - etc).
The problem we ran into was that the Username field in the MembershipProvider couldn't be empty and while in our schema everyone had a UserID (it was their account name), they didn't have a Username that was unique. We had to get around this by generating a GUID and using that. This of course is hidden from the user and a DisplayName attribute from our Users table can be displayed instead.
This was all done via FormsAuthenication (the AD checks were done via LDAP checks). However, an additional layer (a webform) was added with appropriate settings within IIS that provided a means for automatic WindowsAuthentication - we redirect to there in the instance that we feel the user is likely to be internal (based on IP address).
Use standard framework stuff. See http://blogs.teamb.com/craigstuntz/2009/09/09/38390/
You can have an unlimited number of authentication methods attached to one account, the magic is in the FormsAuthentication.SetAuthCookie(userName, createPersistentCookie); statement

Categories