Using Apache CXF to connect to Microsoft Dynamics

A few weeks back a friend of mine approached me with a request. His client was building an application which was supposed to connect to their Microsoft Dynamics CRM. However, they were unable to get any decent response from the CRM Server and needed a little help. My friend and I took up the task of digging into this and solved it, using the Apache CXF framework (which the client was using). In this post, we will explain how to do this. (If you’re just looking for the solution, jump straight down)

Update 17-06-2014: Marnix Klooster used the stuff from this post and elaborated on it over at stackoverflow. Could be a good next step if the stuff below doesn’t help you.

WSDL Analysis & Authentication

The strange thing was that the web service seemed to not respond at all, to any sort of SOAP request, whether faulty or correct. This appears to be Microsoft’s way of saying ‘Hey, you need to authenticate before you can ask me anything’. Not really standard, but I guess we have to live with it.

The documentation of these services leave some things to wish for, so we were forced to delve through the WSDLs and use SoapUI to throw a lot of requests at the services. Eventually the STS authentication service replied with a valid security token. But, Apache CXF seemed to be unable to handle this response. Analysis of the response showed that Microsoft returns an encrypted SAML token, instead of a publicly readable one. Apache CXF is only just able to handle this (see issue CXF-4357), so it takes a snapshot build to authenticate which has been fixed in the 2.6.2 release.

The solution

The solution itself is actually surprisingly simple. The most important part is having the most recent versions of the CXF libraries. The XML-fragment below shows the dependencies required for a Maven project. If you don’t use maven, download these libraries from Apache.

<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-frontend-jaxws</artifactId>
    <version>2.6.2</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-transports-http</artifactId>
    <version>2.6.2</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-ws-security</artifactId>
    <version>2.6.2</version>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-ws-policy</artifactId>
    <version>2.6.2</version>
</dependency>

Most of the ‘magic’ happens in the Spring application context. In the following fragment, we glue together a JAX-WS client for the SOAP service and an STSClient which will take care of obtaining the SAML tokens. The context also includes a policy object telling the CXF framework that Microsoft’s extensions on the WS-Security policy can be ignored. Without this little piece, CXF will reject the configuration, because it cannot parse those policies. Let’s have a look:

<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:jaxws="http://cxf.apache.org/jaxws"
  xmlns:cxf="http://cxf.apache.org/core"
  xmlns:policy="http://cxf.apache.org/policy"
  xmlns:security="http://cxf.apache.org/configuration/security"
  xmlns:http="http://cxf.apache.org/transports/http/configuration"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  
  <jaxws:client name="{http://schemas.microsoft.com/xrm/2011/Contracts}CustomBinding_IOrganizationService" createdFromAPI="true">
    <jaxws:properties>
      <entry key="ws-security.sts.client"  value-ref="crm.sts-client" />
    </jaxws:properties>
  </jaxws:client>
  
  <bean name="crm.sts-client"  class="org.apache.cxf.ws.security.trust.STSClient">
    <constructor-arg ref="cxf"/>
    <property name="wsdlLocation" value="https://adfs.example.com/adfs/services/trust/mex"/>
    <property name="serviceName"  value="{http://schemas.microsoft.com/ws/2008/06/identity/securitytokenservice}SecurityTokenService"/>
    <property name="endpointName"  value="{http://schemas.microsoft.com/ws/2008/06/identity/securitytokenservice}UserNameWSTrustBinding_IWSTrust13Async"/>
    <property name="properties">
      <map>
        <entry key="ws-security.username" value="user1"/>
        <entry key="ws-security.callback-handler" value="net.quies.demo.sts.HardcodedPassword"/>
      </map>
    </property>
  </bean>
  
  <!-- Skip Microsoft extensions -->
  <bean class="org.apache.cxf.ws.policy.IgnorablePolicyInterceptorProvider">
    <constructor-arg>
      <list>
        <bean class="javax.xml.namespace.QName">
          <constructor-arg value="http://schemas.microsoft.com/xrm/2011/Contracts/Services"/>
          <constructor-arg value="AuthenticationPolicy"/>
        </bean>
      </list>
    </constructor-arg>
  </bean>
  
  <cxf:bus>
    <cxf:features>
    <!-- Enables policy support: -->
    <policy:policies/>
    <!-- Enables logging of SOAP messages. -->
    <cxf:logging/>
    </cxf:features>
  </cxf:bus>

</beans>

Note that the crm.sts-client bean contains the key information about the actual services invoked. The WSDL location, service name, endpoint and the username and password. These are all settings you should adjust to your particular environment. Note that it also references a class called HardcodedPassword, which is a very simple and insecure implementation of a CallbackHandler, providing CXF with the password:

public class HardcodedPassword implements CallbackHandler {
  @Override
  public void handle(Callback[] callbacks)
      throws IOException, UnsupportedCallbackException {
    for (Callback c : callbacks) {
      if (c instanceof WSPasswordCallback) {
        WSPasswordCallback passwordCallback = (WSPasswordCallback) c;
        passwordCallback.setPassword("passw0rd");
        continue;
      }
      throw new UnsupportedCallbackException(c);
    }
  }
}

Bringing it all together

With these beans setup, you’re ready to use the XRMServices in your code. Below is a little unit test to demonstrate how you can now create a CXF bus from the Spring context, create a service from it and call the port:

@Test
public void authenticate() throws Exception {
  URL wsdlLocation = new URL("https://xrmservices.exmple.com/XRMServices/2011/Organization.svc?wsdl");
  URL springLocation = new URL("classpath:applicationContext.xml");
  
  SpringBusFactory factory = new SpringBusFactory();
  Bus bus = factory.createBus(springLocation);
  BusFactory.setDefaultBus(bus);
  
  OrganizationService service = new OrganizationService(wsdlLocation);
  IOrganizationService port = service.getPort(IOrganizationService.class);
  
  Entity entity = new Entity();
  port.create(entity);
}

And that’s it. Calling the OrganizationService will now cause the CXF framework to first call the Token service to authenticate itself, obtain a token and use that as a SAML assertion in the resulting SOAP call. Heck, any service you now create from the Bus and that requires SAML authentication, will use the same setup transparently.

Happy coding!

–JH

Special thanks go out to Pascal de Kloe, from Quies.net.

Posted in Java, Spring and tagged , , , , , .