WIF: Tips on Adding a Service Reference to a Claims Aware WCF Service
Windows Identity Foundation (WIF) allows you to federate your web applications and WCF services to use claims based authentication. When you add a service reference to a claims aware WCF service that federates against ADFS 2.0, your client configuration file will include many configuration elements for making WCF calls to both the ADFS server as well as the claims aware WCF service in order to support the WS-Trust protocol. Let us review what is added to the client configuration file and provide a tip on how to make these clients configuration files cleaner.
As I mentioned before there are two WCF service calls that occur; one to ADFS to authenticate the client and get the SAML token, then a second call to the claims aware WCF Service. This requires two WCF bindings that the client will use, one for ADFS and one for WCF.
Start by reviewing the client endpoint configuration in your client configuration file (web.config or app.config):
The Add Service Reference wizard created a single client endpoint, which points to our claims aware WCF Service at the expected address of https://localhost/ClaimsAwareWCFService/Service.svc, which I used for this sample claims aware WCF service. In this client endpoint configuration, we see the contract used, which defines the service methods, parameters and return types. You will also see that in order to call the WCF service the client has to use a customBinding that has a binding configuration name of "WS2007FederationHttpBinding_IService".
Next, we will find that custom binding to talk about some of elements. If you scroll up to the <bindings> section, you will find a section for <customBinding>, which lists all the configured custom bindings. There is only one listed and we can see that it does have the matching name of "WS2007FederationHttpBinding_IService". Good, so now we are looking at the binding configuration for the binding that this client will use to invoke the WCF service.
In this customBinding, look at the current <issuedTokenParameters> section. The <issuedTokenParameters> contains details needed for this WCF client to call out to ADFS first. You can see the <issuer> element that has an address attribute that indicates that this binding configuration will call the /adfs/services/trust/2005/windows endpoint. It is possible that your client configuration defaults to a different ADFS endpoint. We will talk about why in a moment, however please look directly below that first closing </issuedTokenParameters> element, you will see a section that is commented out that looks similar to this:
This commented out section contains a list of all the alternative <issuedTokenParameters> configuration elements that you could use to replace the current <issuedTokenParameters> section. Each of these issuedTokenParameters elements points to a different enabled ADFS endpoint and contains a matching bindingConfiguration that you will also find in the <bindings> section.
Say you did not want to have your WCF client application to authenticate against the /adfs/services/trust/2005/windows endpoint of ADFS… Let us say that instead you would like for your WCF client to use WS Trust 1.3 endpoint at /adfs/services/trust/13/windows endpoint of ADFS. In order to make this change you just have to simply comment out the current <issuedTokenParameters> element and replace it with the exact <issuedTokenParameters> for that specific /adfs/services/trust/13/windows endpoint that you'll see in the commented <alternativeIssuedTokenParameters> section.
This does appear to be many configuration and bindings elements in the client config file, when I only want the client application to authenticate against ADFS at a specific endpoint. The reason this occurs is the result of the binding configuration of your claims aware WCF service. Open up the web.config file of your claims aware WCF service and look at the binding:
Notice that the default federation binding ONLY specifies the <issuerMetadata> element. This element has an address attribute that tells the client where to go get the metadata for the token issuer in order to figure out how to authenticate against that token issuer.
During the Add Service Reference wizard, we download the metadata for WCF service, that metadata tells us there is a federated binding and that there is an issuer, but it only provides the issuer metadata address. Therefore, the Add Service Reference wizard then goes out to download the metadata for ADFS. After it gets the ADFS metadata the wizard ends up generating bindings and <issuedTokenParameters> for every single ADFS enabled endpoint that supports the WS-Trust protocol. This is why you see so many different ways to invoke the ADFS server in the client application's configuration file after running the Add Service Reference wizard. We then select the first endpoint that supports WS-Trust protocol, and this could be any random endpoint enabled in ADFS, where each ADFS endpoint has different requirements in order to call that endpoint; some require further coding, some require configurations within Active Directory for each domain user, etc.
Now open the ADFS 2.0 Management console on your ADFS server. Expand Service -> Endpoints and you will see all the available endpoints that you can use to call into ADFS. Each endpoint has a unique authentication, security and protocol requirement. You can enable and disable them as desired, however when we only specify the issuerMetadata in the Claims Aware WCF Service binding then we will generate client binding details for each ADFS endpoint that supports the WS-Trust protocol.
Developers often run into problems when running their client application immediately after finishing the Add Service Reference wizard. They usually see that their client application is attempting to authenticate against the /adfs/services/trust/2005/certificate or the /certifiatemixed endpoints of ADFS by default. If you simply run the client application without adding code to specify a client certificate that maps to some domain user then the call to those ADFS /certificate* endpoints will fail, but the error message does not provide good guidance on what steps to take next. You will want to pay attention to the default WS-Trust ADFS endpoint that the Add Service Reference wizard selects to authenticate your client with.
If you already know what protocol, authentication type and security mode you want your clients to use to authenticate against ADFS then I recommend including this information in your Claims Aware WCF service binding details before you add a service reference from your client to the WCF service. For our sample scenario, the client application and claims aware WCF service are on the same domain and only users of the domain would run the application so I will configure the client application to authenticate against ADFS using WS-Trust 1.3 protocol over SSL and Windows Authentication. Specifically, I want the client application to authenticate against the /adfs/services/trust/13/windowstransport endpoint highlighted below, as seen in the ADFS 2.0 management console:
As the Claims Aware WCF developer you can suggest your client application to authenticate against that specific /adfs/services/trust/13/windowstransport endpoint by adding the <issuer> element to your WCF federated binding, then add the address attribute and specify the individual ADFS endpoint you want clients to authenticate against ADFS.
Back in the web.config file for your claims aware WCF service add the <issuer> element and point to that /trust/13/windowstransport endpoint as highlighted below:
Now save the changes to the web.config file of the WCF service so that the changes apply to this claims aware service. Next, delete your original service reference from your client application within Visual Studio. After you delete that service reference, then walk through the steps to add a service reference from your client application to the claims aware WCF service again.
Now that you have added the service reference again to this updated claims aware WCF service, look at your updated client configuration file. You will see that it is drastically cleaner and that there are only two bindings listed: One for WCF, which is the ws2007FederationHttpBinding, and then a second binding, the wsHttpBinding which your client application will use to talk to ADFS at that specific /adfs/services/trust/13/windowstransport endpoint. Here is that updated configuration file that the client application will use to call the ADFS server and WCF Service:
Notice how much easier it is to follow how the bindings all work on the client side now. The client endpoint points to the ws2007FederationHttpBinding. The ws2007FederationHttpBinding has an <issuer> element which identifies the binding type and binding configuration required to authenticate against the token issuer, ADFS, and this points to the wsHttpBinding that is also in the configuration file.
Adding the <issuer address=”…”/> element to your claims aware WCF service will help identify a specific endpoint your client applications will use to authenticate against the issuer. There could be a good debate about this because you are creating a claims aware application that is truly only interested in receiving a SAML token from a trusted issuer for authentication. The relying party application should not be concerned if the client used Kerberos, Username/Password, or certificates to authenticate with the token issuer, but it is good to know this feature is available to help clean up your client configuration files and simplify developing client applications to call your claims aware WCF service applications.