Access Control Service v2 is significantly enhanced compared to the first version which only supported OAuth WRAP and Simple Web Tokens. Check out this post from Hervey for a listing of new exciting features. In this post I’ll talk about integrating ACS v2 with web/workflow services (hosted in Windows Server AppFabric) and show you all the steps involved in this process.

To start login into ACS Portal and create a new namespace:

image

Once the namespace is activated, click on the manage link to open the ACS management portal:

image 

Switch to “Application Integration” page which list various endpoints exposed by the ACS

image

Copy the URL of the WS-Federation Metadata endpoint in a new browser window and you would get following error.

image

So our STS instance needs a signing certificate. This certificate is even required to generate Federation metadata document as it is a signed document.  Let’s add a private key signing certificate.

Go to “Certificates and Keys” page and add a token signing certificate.

image

The command to generate a simple self-signed certificate is listed on the page.

image

Now try browsing the Federation metadata and this time you should see the metadata in the browser.

Next create a new Workflow Service in using the “WCF Workflow Service Application” template

image 

Wizard also generates some basic configuration…

Code Snippet
  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3.   <system.web>
  4.     <compilation debug="true" targetFramework="4.0" />
  5.   </system.web>
  6.   <system.serviceModel>
  7.     <behaviors>
  8.       <serviceBehaviors>
  9.         <behavior>
  10.           <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
  11.           <serviceMetadata httpGetEnabled="true"/>
  12.           <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
  13.           <serviceDebug includeExceptionDetailInFaults="false"/>
  14.         </behavior>
  15.       </serviceBehaviors>
  16.     </behaviors>
  17.     <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  18.   </system.serviceModel>
  19.   <system.webServer>
  20.     <modules runAllManagedModulesForAllRequests="true"/>
  21.   </system.webServer>
  22. </configuration>

 

ACS v2 fully integrates with Windows Identity Foundation and this integration is done using the WS-Federation Metadata. When you install WIF SDK, it extends Visual Studio with some additional command like “Add STS Reference” etc.

Right click on the project and Choose the “Add STS Reference” command to launch Federation Utility wizard.

image 

Choose the existing STS option and in textbox specify the Federation metadata URL.

image

If you have used a self-signed signing certificate you would get “chain validation error” in the next step.

image

Disable the “Certificate Chain Validation” and continue to the next step and specify a “Token encryption certificate”

image

Click next to finish the wizard.  The wizard would generate the required configuration to configure this web service to require a SAML token from ACS v2.

Following are some key configuration changes.

  • Default binding for the http scheme is set to ws2007FederationHttpBinding.
<protocolMapping>
  <add scheme="http" binding="ws2007FederationHttpBinding" />
</protocolMapping>
  • WIF is configured on the Web Service using the default service behavior
<serviceBehaviors>
  <behavior>
    <federatedServiceHostConfiguration />
    <serviceMetadata httpGetEnabled="true" />
    <serviceDebug includeExceptionDetailInFaults="false" />
    <serviceCredentials>
      <!--Certificate added by FedUtil.  Subject='CN=localhost', Issuer='CN=Root Agency'.-->
      <serviceCertificate findValue="F7AD5A9DCC35F21FFC691925515F48EB44F5E07A"
                          storeLocation="LocalMachine"
                          storeName="My" x509FindType="FindByThumbprint" />
    </serviceCredentials>
  </behavior>
</serviceBehaviors>

 

Default ws2007FederationHttpBinding is configured to require token from ACS v2

<ws2007FederationHttpBinding>
  <binding>
    <security mode="Message">
      <message>
        <issuerMetadata address="https://alpha2.accesscontrol.appfabriclabs.com/v2/wstrust/mex" />
        <claimTypeRequirements>
          <!--Following are the claims offered by STS 'https://alpha2.accesscontrol.appfabriclabs.com/'.
          <add claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" isOptional="true" />
          <add claimType="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" isOptional="true" />
          <!--<add claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" isOptional="true" />-->
          <!--<add claimType="http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider" isOptional="true" />-->
        </claimTypeRequirements>
      </message>
    </security>
  </binding>
</ws2007FederationHttpBinding>

And finally Microsoft.IdentityModel configuration is generated to configure various WIF token validation configuration

<microsoft.identityModel>
  <service>
    <audienceUris>
      <add value="http://localhost:53250/Service1.xamlx" />
    </audienceUris>
    <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry,
                        Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
      <trustedIssuers>
        <add thumbprint="F7AD5A9DCC35F21FFC691925515F48EB44F5E07A" name="https://alpha2.accesscontrol.appfabriclabs.com/" />
      </trustedIssuers>
    </issuerNameRegistry>
    <certificateValidation certificateValidationMode="None" />
  </service>
</microsoft.identityModel>

 

Ok let’s now create a console client for this web service. Remember we haven’t configured anything in the ACS yet other than specifying a signing cert.

image 

Choose the “Add Service Reference” to add a reference to this service

image

When you click Ok, SvcUtil.exe will detect that this service requires a token from ACS and will go and do metadata exchange to figure out various endpoints supported ACS v2. It will then generate all the configuration required to talk to ACS and the service. Because ACS supports multiple token issuance endpoints, svcutil.exe simply picks the first one. We need to change that manually to the one we want to use for our scenario. For this example I’m going to use the UserName endpoint so I’ll change following section of the binding:

<issuedTokenParameters keySize="256" keyType="SymmetricKey" tokenType="">
    <additionalRequestParameters>
        <trust:SecondaryParameters xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
            <trust:KeyType xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey</trust:KeyType>
            <trust:KeySize xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">256</trust:KeySize>
            <trust:Claims Dialect="http://schemas.xmlsoap.org/ws/2005/05/identity"
                xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
                <wsid:ClaimType Uri="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"
                    Optional="true" xmlns:wsid="http://schemas.xmlsoap.org/ws/2005/05/identity" />
                <wsid:ClaimType Uri="http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
                    Optional="true" xmlns:wsid="http://schemas.xmlsoap.org/ws/2005/05/identity" />
            </trust:Claims>
            <trust:KeyWrapAlgorithm xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p</trust:KeyWrapAlgorithm>
            <trust:EncryptWith xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://www.w3.org/2001/04/xmlenc#aes256-cbc</trust:EncryptWith>
            <trust:SignWith xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://www.w3.org/2000/09/xmldsig#hmac-sha1</trust:SignWith>
            <trust:CanonicalizationAlgorithm xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://www.w3.org/2001/10/xml-exc-c14n#</trust:CanonicalizationAlgorithm>
            <trust:EncryptionAlgorithm xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://www.w3.org/2001/04/xmlenc#aes256-cbc</trust:EncryptionAlgorithm>
        </trust:SecondaryParameters>
    </additionalRequestParameters>
    <issuer address="https://alpha2.accesscontrol.appfabriclabs.com/v2/wstrust/13/issuedtoken"
        binding="customBinding" bindingConfiguration="https://alpha2.accesscontrol.appfabriclabs.com/v2/wstrust/13/issuedtoken" />
    <issuerMetadata address="https://alpha2.accesscontrol.appfabriclabs.com/v2/wstrust/mex" />
</issuedTokenParameters>

to

<issuedTokenParameters keySize="256" keyType="SymmetricKey" tokenType="">
    <additionalRequestParameters>
        <trust:SecondaryParameters xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
            <trust:KeyType xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey</trust:KeyType>
            <trust:KeySize xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">256</trust:KeySize>
            <trust:Claims Dialect="http://schemas.xmlsoap.org/ws/2005/05/identity"
                xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
                <wsid:ClaimType Uri="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"
                    Optional="true" xmlns:wsid="http://schemas.xmlsoap.org/ws/2005/05/identity" />
                <wsid:ClaimType Uri="http://schemas.microsoft.com/ws/2008/06/identity/claims/role"
                    Optional="true" xmlns:wsid="http://schemas.xmlsoap.org/ws/2005/05/identity" />
            </trust:Claims>
            <trust:KeyWrapAlgorithm xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p</trust:KeyWrapAlgorithm>
            <trust:EncryptWith xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://www.w3.org/2001/04/xmlenc#aes256-cbc</trust:EncryptWith>
            <trust:SignWith xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://www.w3.org/2000/09/xmldsig#hmac-sha1</trust:SignWith>
            <trust:CanonicalizationAlgorithm xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://www.w3.org/2001/10/xml-exc-c14n#</trust:CanonicalizationAlgorithm>
            <trust:EncryptionAlgorithm xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://www.w3.org/2001/04/xmlenc#aes256-cbc</trust:EncryptionAlgorithm>
        </trust:SecondaryParameters>
    </additionalRequestParameters>
  <issuer address="https://alpha2.accesscontrol.appfabriclabs.com/v2/wstrust/13/username"
          bindingConfiguration="https://alpha2.accesscontrol.appfabriclabs.com/v2/wstrust/13/username" binding="ws2007HttpBinding" />
</issuedTokenParameters>

Notice I have changed the issuedtoken endpoint to a username endpoint.

Let’s create a basic client and run it.

class Program
{
    static void Main(string[] args)
    {
        var client = new ServiceReference1.ServiceClient();
        Console.WriteLine(client.GetData(33));
    }
}

Got following exception:

System.ServiceModel.Security.SecurityNegotiationException: SOAP security negotiation with 'http://localhost:53250/Service1.xamlx' for target 'http://localhost:53250/Service1.xamlx' failed. See inner exception for more details. ---> System.IdentityModel.Tokens.SecurityTokenValidationException: The X.509 certificate CN=localhost chain building failed. The certificate that was used has a trust chain that cannot be verified. Replace the certificate or change the certificateValidationMode. A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider.

Because I’m using self-signed certificate so trust chain is failing. For now I’ll just disable the “trust chain validation” by adding following endpoint behavior to the client config.

<behaviors>
  <endpointBehaviors>
    <behavior>
      <clientCredentials>
        <serviceCertificate>
          <authentication certificateValidationMode="None"/>
        </serviceCertificate>
      </clientCredentials>
    </behavior>
  </endpointBehaviors>
</behaviors>

Running it again, I get a different error:

System.ServiceModel.Security.SecurityNegotiationException: SOAP security negotiation with 'https://alpha2.accesscontrol.appfabriclabs.com/v2/wstrust/13/username' for target 'https://alpha2.accesscontrol.appfabriclabs.com/v2/wstrust/13/username' failed. See inner exception for more details. ---> System.InvalidOperationException: The username is not provided. Specify username in ClientCredentials.
Of course I have chosen the userName endpoint on the ACS so WCF is asking about username/password for talking to this endpoint. Let’s specify one.

static void Main(string[] args)
{
    var client = new ServiceReference1.ServiceClient();
    client.ClientCredentials.UserName.UserName = "test";
    client.ClientCredentials.UserName.Password = "test";
    Console.WriteLine(client.GetData(33));
}

Running the sample again, this time a call was indeed issued to ACS and I get an error from ACS.

System.ServiceModel.Security.MessageSecurityException: An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail. ---> System.ServiceModel.FaultException: ID3034: Authentication failed.
   --- End of inner exception stack trace ---

So ACS has tried to validate the incoming userName/password and it can’t find a match as there is no such userName exist.  Identities owned and managed by ACS can be created using the “Service Identities” link.

Go back to the portal and click on the “Service Identities” link to add a new service identity and name it as “test” (same as our userId)

image

Add a “Password Credential” with password value again set to “test” (same as password used by client app)

image

Running the client again: this time I get a different error:

System.ServiceModel.Security.MessageSecurityException: An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail. ---> System.ServiceModel.FaultException: ID3082: The request scope is not valid or is unsupported.

So the authentication with ACS was successful but it can’t find a “Relying Party” matching to my request. Please note, client is making a token issuance request for the workflow service which is identified as “http://localhost:53250/Service1.xamlx”.

So let’s add a new Relying Party once again using the management portal.

image 

By running the application, I get following error.

System.ServiceModel.Security.MessageSecurityException: An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail. ---> System.ServiceModel.FaultException: ID3037: The specified request failed.

Now this error doesn’t tell us much but there are couple of things we need to do. First of all we created a “Default Rule group” as part of our “Relying Party” but we didn’t added any rules to it. So let’s add a Passthrough rule.

image

For Web Services federation, ACS v2 by default uses Holder of Key confirmation method so a public key certificate is required for encrypting the proof key. This certificate MUST match with the private key certificate configured for the web service (aka Relying Party (RP)).

Ok let’s add an encryption certificate using the “Certificates and Keys” page. Remember to select your corresponding RP.

image

Running the application and hurray it runs without error and I can see the output.

image 

In this sample, I have used a Workflow Service but these exact steps works for a code based WCF service as well. It’s a LONG post but hopefully someone will find it useful as it took me some time to figure few hidden gems (especially the holder of key stuff).