Sometimes you are in the situation where you have Windows-based users, but the rest of the application architecture is token-based (e.g. using OpenID Connect or WS-Federation). As long as these users stay in your “token-based world” everything is fine. But if you have impersonate those users to talk to Windows authentication-only service (often that’s SQL Server or a file share), you are in trouble.
Historically this was only possible if you were using Kerberos all the way through – and also only over two hops (e.g. browser to web server – web server impersonating browser user to SQL Server). In you token-based architecture there is no Kerberos.
That’s the reason why Microsoft added extensions to Kerberos (actually already back in Windows Server 2003 days) that make it possible to transition from a non-Windows authentication method to a native Windows token (called S4U – services for user).
In essence there is a way to construct a WindowsIdentity just by passing in a Windows account name and get back a Windows token. When you have a WindowsIdentity, you can call Impersonate and impersonate the user. So really the only thing you need to do, is to be able to map the token user back to a Windows user (e.g. via a UPN claims or similar). Job done !?
Hold on – if that would be possible, wouldn’t that completely subvert the Windows security system? Well it is possible, but there are a number hoops you have to jump through.
This comes up every once in a while, and it is always hard to remember all the details. Since I just did that two weeks ago – here’s a brain dump.
- First you have to install the “Claims to Windows Token Service” – that’s a Windows service that runs as SYSTEM and thus has enough privileges to create a Windows token that can be impersonated. This service is hidden behind a Windows Server feature called “Windows Identity Foundation”.
- You need to configure the Windows service. Since this is such a highly privileged operation (think about it – this service can create Windows tokens for arbitrary users), you need to specify who is allowed to connect to it. This is done in a .config file in the same directory as the service binaries. Add the Windows account of your application that needs to use the service.
- Your application account needs an SPN (use the setspn tool to create one).
- You need to configure constrained delegation for the application account. This is done in the “Active Directory Users & Computers” tool. On the property pages – use the Delegation tab to configure this account to be “trusted for delegation to specified services only”. Furthermore set the “Use any authentication protocol” option and configure the list of services to which the Windows user identities can be delegated to.
The last step is a tricky part – you also need to create SPNs for the services you want to connect to. The format of theses SPNs depends on the type of service. This document is very helpful to learn about the format for SQL Server.
If all else fail – I always use Wireshark to trace the Kerberos traffic. This will include the requested SPN and makes it easier to figure out the right format.
If everything is configured correctly – you can use the S4UClient class to get the Windows token for the account you want to impersonate:
var id = S4UClient.UpnLogon(“user@domain”);
The easiest way to get S4UClient is by using Nuget.
Afterwards you can impersonate the Windows identity to make the service request:
// call SQL Server etc..
If you get errors saying something like “NT AUTHORITY\ANONYMOUS” authentication has failed authentication, the delegation configuration is incorrect (most possibly the wrong SPN is configured).
Why is this so complicated?
As I said before, this feature allows constructing a valid Windows identity without having to know the password of the user. IOW – this allows impersonating your CEO and make changes to salary database. That’s why.
So you want to make sure that your application, the application account and the server you run on is properly secured.
(While writing this I always had to think about these two tweets from Enno: this and this.)
Addition Some readers have pointed out, that the “Claims to Windows Token Service” (or short C2WTS) is not really necessary. You just need to give your application account the “Act as part of the Operating System” privilege, and everything is working fine.
That’s true – but you don’t want to give your application that privilege (that’s in essence SYSTEM) – and that’s the actual reason why the C2WTS service was created in the first place.
Nice post ! If we use this, after the impersonation, we could call the AD FS STS to get a token from the kerberos endpoint for this user I guess ? Do you think this could work ?
Don’t know. try it.
I am wondering if this is still the only way to do this, since this article is 20 months old. I want to create an endpoint for a mobile app and use oauth2 as authorization mechanism. But on the server I want to use the windows identity of the user to access sql server. Is there a better option or should I try this?
That’s still a valid (and the only really) approach if you need to transition back to a Windows identity for a user.
I think that you can use WindowsIdentity constructor with upn parameter
var wi = new WindowsIdentity(upn);
that will do the S4UClient calls in the background. I found this to be especially helpful for .net core app.
I have one question. Is it possible that the WindowsIdentity to have ImpersonationLevel higher than Identify. Seems that with Identify Level you cannot do SQL server access. You need Impersonation or even Delegation Level.
Can you give your thoughts?