We all know the following limitations about Windows Identity Foundation (WIF) and passive (browser) federation protocols, right?
- WIF does not support SAML2.0 protocol (SAML2P)
- There is a WIF extension out there to support SAML2P but it is a technology preview
- WIF does support SAML2.0 (SAML2) tokens
- WS-Federation conveys SAML1.1 tokens
Therefore, unless you use the WIF extension there is no way of getting a SAML2 token into a Relying Party (RP) application.
Well, did you know the following?
- WS-Federation responses convey WS-Trust RequestSecurityTokenResponse (RSTR) elements containing security tokens
- The RSTR element is not tied into a particular token format
- i.e. – it is a <xs:any> element in the schema
This means that it is perfectly valid for a WS-Federation response to convey a SAML2.0 token and it is only because WS-Federation existed before SAML2P that it became a de facto convention for WS-Federation to convey SAML1.1 tokens.
So, it IS possible to get a SAML2 token into a RP application as long as it is possible to get a SAML2 token into a WS-Federation response.
To achieve this I am using Active Directory Federation Services (ADFS 2.0) as it is simple to integrate with WIF and it understands SAML2P, and therefore SAML2 tokens.
The setup is a Windows Server 2012 domain controller with the ADFS 2.1 role installed. All code was written using Visual Studio 2012 and .NET framework 4.5 although the same could be achieved with Windows Server 2008 R2, Visual Studio 2010 and .NET framework 4.0.
As discussed, this won’t work:
But this should:
The strategy is to place an intermediary Relying Party Security Token Service (RP-STS) between RP and ADFS which performs protocol transition from SAML2P to WS-Federation.
To make things a little simpler, I have applied the following restrictions to the RP-STS:
- It is not a signing authority
- That is, it cannot create authoritative tokens itself but simply passes on tokens received from other providers
- It does not validate either the protocol wrapper or token itself
- This would involve a lot of extra code and the RP is going to do it anyway
- The SAMLResponse wrapper element received from ADFS containing a SAML2 token is not digitally signed
- The SAML2P specification does allow for a digital signature though
- The RP-STS only recognises messages transmitted using the SAML 2.0 HTTP redirect and POST bindings
The RP-STS itself is simply a ASP.NET HTTP module hosted in IIS. The first event in the ASP.NET pipeline, BeginRequest, is handled as there is nothing more for the module to do than parse tokens.
The flow of events is as follows:
The actual code is shown below:
This ‘almost’ works in that the SAML2 token is successfully extracted from the ADFS response and plugged into a WS-Federation response which is forwarded to the RP website.
However, it turns out that WIF takes exception to the token! You will get the following error message:
“ID4154: A Saml2SecurityToken cannot be created from the Saml2Assertion because it contains a SubjectConfirmationData which specifies an InResponseTo value. Enforcement of this value is not supported by default. To customize SubjectConfirmationData processing, extend Saml2SecurityTokenHandler and override ValidateConfirmationData.”
To avoid this error, do the following:
- Create a custom SecurityTokenHandler implementation derives from the default Saml2SecurityTokenHandler
- Override the ValidateConfirmationData method
- Remove the default Saml2SecurityTokenHandler in the <system.identityModel> config section
- Add in the new implementation
In fact, upon inspection of the SAML2 token, the InResponseTo attribute referred to above is actually inappropriate for the RP website as it contains the URL for the RP-STS website. However, as WIF by default block tokens containing this attribute, there probably isn’t much code that would care about the value contained within the attribute. That said, this warrants careful consideration if you are using this template for a production scenario.
I am pretty sure that this inaccuracy in the URL could be circumvented by combining the RP and RP-STS websites together so that they exist under a common domain name (I will investigate this and post my findings). The conversion from WS-Federation request to SAMLRequest could take place in the OnRedirectingToIdentityProvider event handler and conversely, the conversion from ADFS SAMLResponse to WS-Federation response could take place in a HTTP module which fires before the default WIF pipeline takes over.
I have shown that it is possible to use relatively few lines of code to facilitate the transmission of SAML2 tokens into a standard WIF protected website by transitioning to/from WS-Federation to SAML2P federation protocols.
I believe that in the absence of proper SAML2P support in Windows Identity Foundation this approach, with a bit of work to bring the code up to production standards, is a viable one.