In the previous post we saw how easy it is to get basic Relying Party (RP) and Identity Provider (IdP) websites up and running using a minimal amount of coding with Windows Identity Foundation (WIF).
I now want to extend these examples.
In this post I will look at how the RP website can be enhanced, not by adding to the code already written, but by removing it altogether! What I mean by that is the problem we have been trying to solve with custom code has already been solved and incorporated into WIF, and so there is no point in reinventing the wheel.
As a quick reminder, here is the scenario we are currently concentrating on:
The RP application as it currently stands is lacking in a number of areas:
The federation utility tool (FedUtil) is added to Visual Studio through with the WIF SDK. Using this tool it is possible to create new applications, or modify existing ones, so that they are claims aware with a good degree of security and reliability.
We start by modifying the RP application created in the previous post:
That is all that is required to set up the RP application. NB – a new STS project has also been created but ignore it for now.
The outcome of these steps is an update to the web.config file for the RP website. In order to understand what has been added and therefore what has been gained, it is instructive to go through each config file change.
The first addition is a reference to the assembly that enables the microsoft.identityModel config section to be deserialized:
<configSections> <section name="microsoft.identityModel" type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /></configSections>
<location path="FederationMetadata"> <system.web> <authorization> <allow users="*" /> </authorization> </system.web></location>
<authorization> <deny users="?" /></authorization>
Things now start to get more interesting with the addition of two HTTP modules:
<modules> <add name="WSFederationAuthenticationModule" type="Microsoft.IdentityModel.Web.WSFederationAuthenticationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="managedHandler" /> <add name="SessionAuthenticationModule" type="Microsoft.IdentityModel.Web.SessionAuthenticationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="managedHandler" /></modules>
The WSFederationAuthenticationModule module encapsulates all of the protocol logic for the WS-Federation Passive Requestor Profile, including:
The SessionAuthenticationModule creates a cookie containing a session token describing the IClaimsPrincipal instance identified by the signin process. Upon subsequent requests the IClaimsPrincipal is restored from the cookie and made available to the main application.
Finally, the microsoft.identityModel config section is included, within which a <service> instance is placed (NB - The FedUtil tool doesn’t address the entire microsoft.identityModel schema but an excellent overview is given here).
<microsoft.identityModel> <service> <audienceUris> <add value="https://wifrelyingparty/" /> </audienceUris> <federatedAuthentication> <wsFederation passiveRedirectEnabled="true" issuer="http://WIFRelyingParty_STS/" realm="https://wifrelyingparty" requireHttps="false" /> <cookieHandler requireSsl="true" /> </federatedAuthentication> <applicationService> <claimTypeRequired> <!--Following are the claims offered by STS 'http://localhost:30920/WIFRelyingParty_STS/'. --> <!--Add or uncomment claims that you require by your application and then update the federation metadata of this application.--> <claimType type="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" optional="true" /> <claimType type="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" optional="true" /> </claimTypeRequired> </applicationService> <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"> <trustedIssuers> <add thumbprint="55B7A7A0CE02912B806F91460A3FB5E200C0F83E" name="http://localhost:30920/WIFRelyingParty_STS/" /> </trustedIssuers> </issuerNameRegistry> </service></microsoft.identityModel>
The <audienceUris> collection mandates that the RP website will only trust tokens whose audience restriction matches the given Uri. In the case of SAML 1.1 tokens, the intended audience set for the token is located within the <AudienceRestrictionCondition> element:
<saml:Assertion MajorVersion="1" MinorVersion="1" AssertionID="_37ab6cb9-6c53-4df7-8472-4051bab5d3bd" Issuer="PassiveSigninSTS" IssueInstant="2010-09-07T19:03:03.279Z" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"> <saml:Conditions NotBefore="2010-09-07T19:03:03.279Z" NotOnOrAfter="2010-09-07T20:03:03.279Z"> <saml:AudienceRestrictionCondition> <saml:Audience>https://wifrelyingparty</saml:Audience> </saml:AudienceRestrictionCondition> </saml:Conditions> ......</saml:Assertion>
The <federatedAuthentication> element contains several properties each of which are:
The <cookieHandler> element describes various attributes about the RP environment concerning the cookie that will be used by the federation modules. The only default attribute included by the FedUtil tool is @requireSsl which indicates whether or not the RP website must be secured with SSL.
The <applicationService> element contains a list of claims that the issuer must supply in a token, although the optional attribute can be set. In fact this list of required claims is purely descriptive and by default no error occurs if a mandatory claim is missing. However, this list can be used by a ClaimsAuthenticationManager implementation to enforce mandatory claims.
The <issuerNameRegistry> element is used to verify the digital signatures of tokens received from issuers. i.e – only those tokens that contain an entry in the registry will be considered valid. The default implementation is the Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry but a custom implementation can be plugged in if required. e.g – the registry may be the local machine certificate store.
Finally, what does all of this mean for the RP code written in the previous post? As a quick reminder, here is the code behind class for content.aspx:
protected void Page_Load(object sender, EventArgs e){ if (HttpContext.Current.Request.Form[WSFederationConstants.Parameters.Result] != null) { // This is a response from the STS SignInResponseMessage mess = WSFederationMessage.CreateFromNameValueCollection( WSFederationMessage.GetBaseUrl(HttpContext.Current.Request.Url), HttpContext.Current.Request.Form) as SignInResponseMessage; // DO PRESENTATION LAYER STUFF } else { // This is a new request SignInRequestMessage mess = new SignInRequestMessage( new Uri("http://localhost/WIFIdentityProvider/Login.aspx"), "https://WIFRelyingParty", http://localhost/WIFRelyingParty/Content.aspx); HttpContext.Current.Response.Redirect(mess.WriteQueryString()); }}
protected void Page_Load(object sender, EventArgs e){ // DO PRESENTATION LAYER STUFF}
In summary, this article demonstrates that it is possible to make RP applications claims aware with a high degree of security and reliability using the federation utility tool. The fact that all of the code for enabling federation is hidden away in a set of HTTP modules means that the ASP.NET developer need only be concerned with the presentation layer.
This post concludes a 2 part series that covered the WS-Federation Passive Requestor Profile and how it is enabled by Windows Identity Foundation.
Written by Bradley Cotier