Why you should not use SOAP Headers

container

In the project I am working on right now we use apache XCF and Spring to provide a SOAP service to our customers. As part of the messages, there is a userid/password combo telling the application which user sent the request. I struggled with that today because I think that userid/password info should actually be in the SOAP Header, cleaning up my API, enable me to implement different authentication techniques in the future and generally be more "compliant" to the SOAP standard. Boy was I wrong.

First I needed to figure out how to get the userid/password info into the Header. Apache has [some information](https://cxf.apache.org/docs/ws-security.html) on this which is supposed to be "very good" according to the web but I had quite a bit of trouble because it misses quite a bit of context.

At first, it took me ages to find out that I need to add this to my maven pom.xml file to actually "get" the security stuff onto my classpath:

[sourcecode type="xml" gutter="false"] org.apache.cxf cxf-rt-ws-security 2.5.1 [/sourcecode]

I could find NO tutorial online telling me this, so I guess I saved you a good 20 minutes of searching right there.

Then, I needed to change the Spring configuration to include the mysterious "WSS4JInInterceptor" of which nobody understands what it does because it is an overdesigned elephant which implements obscure standards nobody seems to be using:

[sourcecode type="xml" gutter="false"] <jaxws:endpoint id="myCoolSOAPService" implementor="#myCoolService" address="/mycoolservice"> jaxws:inInterceptors </jaxws:inInterceptors> </jaxws:endpoint>

[/sourcecode]

As you can see, the WSS4JInterceptor is configured to accept Usernames and passwords (albeit in deeply, convoluted nested elements adding humongous amounts of XML bloat to your Header).

To be able to take the userid/password combo I dutifully started writing my password handler:

[sourcecode type="java" gutter="false"] public class UseridPasswordCallback implements CallbackHandler {

 @Override
 public void handle(Callback[] arg0)
             throws IOException, UnsupportedCallbackException {
      // Not implemented yet
 }

} [/sourcecode]

You can see that my handle(Callback) method is not implemented. And there is a good reason for that.

Just before implementing all the code, I checked the wiring of all this stuff. The application started up beautifully, and I pointed SOAPUI to the WSDL to generate some example requests. To my horror, SOAPUI did not generate example headers. For some reason, the whole CXF stack just "forgets" to put that info in the WSDL, or maybe the specs don't leave room to have that information in the WSDL, I don't know.

So I set out to see how to build that Soap Security header. It was nuts. Not only did I need to figure this out from crude and incomplete examples, reverse engineer xsd's, and hand-craft it into the SOAPUI messages, I also had to deal with the awful error messages that gave me "Could not read XMReader" and the very helpful "ns1:InvalidSecurity".

After an afternoon of absolute madness on something that should actually be infinitesimally simple, I gave up. I added the userid/password back into the functional calls (as part of my method signatures). And for a couple of very good reasons:

  1. The first an most important one: If I as a developer have this much trouble wading to the poorly documented, underutilized, astrophysics-level complexity just to add a freakin' userid/password field to a SOAP message, how can I expect my users to ever accomplish this without rage-calling our helpline every 4 seconds?
  2. By making the fields part of the API, they become part of the WSDL, which immediately makes it clear what to add and where. SOAPUI even generates nice placeholders for it.
  3. I added an Aspect with an around advice intercepting the userid/password combo, use it to authenticate the user and immediately remove the password from memory. Simple, readable, effective and easily debuggable for any developer on the project.
  4. The pom.xml and the Spring configuration immediately become much simpler and gets rid of a few idiot acronyms in the process.

So there. Want to do SOAP Security as it was intended? Do yourself and your customers a favor: Don't.