Pablo wrote a post yesterday giving some background information on how session tokens are protected in WIF – here some additional info for WCF:
The ws* bindings in WCF establish a security session by default (via WS-SecureConversation). This has some implications, e.g.
- You end up with a stateful service – or more important – with a stateful programming model. You have all the typical session “problems” like faulted sessions, timeout, retries etc…
- By default SecureConversation only transmits a session identifier (like a ASP.NET session cookie) – the actual session is stored in-memory at the server. Not good for load balancing.
When you want to use WCF in a load balanced environment (e.g Azure) – you have to change the default behavior – you basically have two options:
- Turn off SecureConversation all together. This has the advantage of being stateless (at least in that part of the communication). But this also means, that the bootstrap (SAML) token will get parsed on every request – this includes invoking the ClaimsAuthenticationManager. This might have performance implications – but depends on your scenario.
- Force WCF into “cookie mode”. This means that the complete IClaimsPrincipal (after ClaimsAuthenticationManager has run) gets serialized and round-tripped in the SOAP header.
Turning off SecureConversation
Unfortunately WCF 3.5 does not directly allow that on the standard federation bindings. You would need to create a custom binding that uses an authentication mode of IssuedTokenOverTransport (for mixed mode) or IssuedTokenForCertificate (for message security).
In .NET 4 you can simply set establishSecurityContext to false on the standard ws-fed binding.
Forcing WCF into cookie mode requires a custom binding. The “trick” here is to set requireSecurityContextCancellation to false – which is just a fancy name for “serialize the context into the message”. Here’s the binding I am using (mixed mode security):
<issuerMetadata address=”https://…” />
As Pablo points out in his post, the session cookie must be protected somehow. The standard WIF behavior is to the DPAPI user key. This key cannot be easily shared between nodes in a cluster (unless the nodes are all domain members and roaming profiles are activated). Another more explicit (and practical) option is to use an RSA key. Most typically you would feed your SSL certificate or the certificate used to decrypt incoming tokens into the following session token handler:
public class WebFarmSessionSecurityTokenHandler : SessionSecurityTokenHandler
public WebFarmSessionSecurityTokenHandler(X509Certificate2 protectionCertificate)
private static ReadOnlyCollection<CookieTransform> CreateRsaTransforms
var transforms = new List<CookieTransform>()
One way of wiring up the above handler would be a service host factory for the WIF enabled WCF service. If you want to put a little more work in it you can also make the handler configuration friendly (see here).
In general I’d recommend watching Hervey’s excellent talk from PDC09 about WIF in load balanced environments (e.g. Azure).