In a domain environment it is really useful to switch user contexts in a web application.
This could be if you are needing to log in with credentials that have elevated permissions
(or vice-versa) or just needing to log in as another user.
It’s pretty easy to do this with Windows Identity Foundation and Claims Authentication.
When the WIF framework is installed, a service is installed (that is off by default)
that can translate Claims to Windows Tokens. This is called (not surprisingly)
the Claims to Windows Token Service or (c2WTS).
Following the deploy-with-least-amount-of-attack-surface methodology, this
service does not work out of the box. You need to turn it on and enable which
user’s are allowed to impersonate via token translation. Now, this doesn’t mean
which users can switch, it means which users running the process are allowed to switch.
E.g. the process running the IIS application pools local service/network service/local
system/etc (preferably a named service user other than system users).
To allow users to do this go to C:\Program Files\Windows Identity Foundation\v3.5\c2wtshost.exe.config
and add in the service users to <allowedCallers>:
<windowsTokenService>
<!--
By default no callers are allowed to use the Windows
Identity Foundation Claims To NT Token Service.
Add the identities you wish to allow below.
-->
<allowedCallers>
<clear/>
<!-- <add value="NT AUTHORITY\Network Service"
/> -->
<!-- <add value="NT AUTHORITY\Local Service" />
–>
<!-- <add value="nt authority\system" /> –>
<!-- <add value="NT AUTHORITY\Authenticated Users"
/> -->
</allowedCallers>
</windowsTokenService>
You should notice that by default, all users are not allowed. Once you’ve done
that you can start up the service. It is called Claims to Windows Token
Service in the Services MMC snap-in.
That takes care of the administrative side of things. Lets write some code.
But first, some usings:
using System;
using System.Linq;
using System.Security.Principal;
using System.Threading;
using Microsoft.IdentityModel.Claims;
using Microsoft.IdentityModel.WindowsTokenService;
The next step is to actually generate the token. From an architectural perspective,
we want to use the UPN claims type as that’s what the service wants to see.
To get the claim, we do some simple LINQ:
IClaimsIdentity identity = (ClaimsIdentity)Thread.CurrentPrincipal.Identity;
string upn = identity.Claims.Where(c => c.ClaimType == ClaimTypes.Upn).First().Value;
if (String.IsNullOrEmpty(upn))
{
throw new Exception("No UPN claim found");
}
Following that we do the impersonation:
WindowsIdentity windowsIdentity = S4UClient.UpnLogon(upn);
using (WindowsImpersonationContext ctxt = windowsIdentity.Impersonate())
{
DoSomethingAsNewUser();
ctxt.Undo(); // redundant with using { } statement
}
To release the token we call the Undo() method, but if you are within a using { }
statement the Undo() method is called when the object is disposed.
One thing to keep in mind though. If you do not have permission to impersonate
a user a System.ServiceModel.Security.SecurityAccessDeniedException will be thrown.
That’s all there is to it.
Implementation Details
In my opinion, these types of calls really shouldn’t be made all that often.
Realistically you need to take a look at how impersonation fits into the application
and then go from there. Impersonation is pretty weighty topic for discussion,
and frankly, I’m not an expert.