Having worked on enterprise level applications for a number of years, it has always amazed me how often users are forced to login, multiple times, to different applications in the same domain.
From a user experience point of view, this is an irritating scenario. Using Integrated Windows Authentication (IWA) can avoid this problem and facilitate single sign on.
This by itself is enough reason to use IWA but, from an IT support point of view, you can also gain the following benefits:
- It can avoid the hard coding of admin passwords in databases, registry settings, configuration files and, dare I say it, in application code!
- It can avoid the maintenance nightmare of maintaining multiple User tables
In addition, it can also avoid limiting access to web services – the onus is on the user having access to the data, not access to the web service. The web service will still return an access exception from the data layer. This is based on the scenario that the web service is purely a data transportation mechanism (as is often the case in an SOA) and not implementing any business rules hence making access irrelevant.
The main point of this article is to address the 'double hop' issue when using IWA in an architecture that involves at least one Server 'Hop', i.e., server to server communication.
This solution uses the assumption that each application will have its own AD Group. Each application AD group will contain the users that are allowed access to the application. In this case, we are using a mapping application with an AD Group called Domain\ClearAppMapping.
This currently applies to the following architecture:
- Windows Client Application [Client] --> ASP Web Service [Server 1] --> SQL Server DB [Server 2]
An update that will be very similar will be included for the following architectures in the future:
- ASP.NET Web Site [Server 1] --> ASP Web Service [Server 2] --> SQL Server DB [Server 3]
- Silverlight [Client 1] --> WCF [Server 2] --> SQL Server DB [Server 3]
Administration rights will be required for access to the Active Directory(AD) and to IIS.
Windows Client Application [Client]
The first stage of the process is to pass the credentials from the client application.
The client application needs a web reference to act as a proxy to the appropriate web service. In this example, we have a controller project that controls the retrieval of data [as in the pattern Model-View-Controller]. If you are not splitting your code in this way, then you would do this in your EXE project.
In the appropriate controller, we need to add a line of code to tell the web service to use the default credentials, i.e., the current user. This is the line
UseDefaultCredentials = true; in the example below:
ASP Web Service [Server 1]
Stage 2 of the process is making sure that the client credentials are received at the first server. To do this, we need to make a couple of changes to the configuration file of the web service:
We tell the web service to use Windows authentication mode, which is the default.
We also have to tell the web service to use impersonation. This tells the web service to use the credentials that it has been passed, rather than the default IIS user, for the next part of the communication.
The web services server needs to be able to communicate with the database server. To do this, we need to enable this server to be trustable, i.e., allow it to request services from another computer. To do this, we need to find the appropriate server in AD and set the following property:
In IIS, we need to remove anonymous access from the security settings. To do this, select the web service in IIS.
Right click, select properties and select the Directory Security Tab.
Click Edit on the group box for Anonymous access and authentication control and deselect the Anonymous access check box, making sure that only IWA is selected.
It is worth noting that by using impersonation, we limit the benefits of application connection pooling as each connection string will be different. If we use the same credentials, i.e., the same connection string, for each call, pooling will be implemented and (theoretically) only one connection will be used.
I have been advised by my DBA that this shouldn't be a problem as SQL connections are unlimited and you are only limited by the resources on the server that the database sits.
This architecture is therefore less scalable and it may be worth considering a different mechanism when dealing high user, high transaction applications – you may want to look at using Service Accounts that manage the server hops for you. This is likely to involve hard coding a user account and password as so:
SQL Server DB [Server 2]
Stage 3 of the process is receiving the credentials and then allowing the user access to the underlying data.
None of the applications that we are writing should be accessing data directly. All data access should be via Stored Procedures. This enables our data to be secured appropriately.
When setting up the SQL Server side of things, we need to create a user based on the applications AD Group:
We need to also create a database role for this application:
This role will include the AD Group as its member:
You will then apply this role to the stored procedures that are used for the application. Never give the user direct access to the database!
That’s it. Your users should only ever have to login to the operating system to use (securely) their applications.
Points of Interest
NOTE: IWA may not work properly when you have users in multiple AD forests, where the forests are different versions. I have tested this with a Server 2000 forest and a server 2003 forest.
An example of this is if you are porting users to a new AD at different rates. This means you could have 10 users who are coming from a 2003 forest and 10 users coming from a 2000 forest. The users in the new forest will be unable to access the data as their credentials won’t be transferred from server to server.
- 11th October, 2011: Initial post