Will
Cannings

I recently needed to pass a WSSE authentication header with some SOAP messages sent from a Rails app to a government service. Ruby’s native SOAP library is soap4r. It’s featureful, but under-documented, so it took some fiddling to get WSSE to work.

WSSE is a pretty simple standard for sending a username and password to a service in a SOAP message’s header. The layout is:

<wsse:Security xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/07/secext" soap:mustUnderstand="0">
  <wsse:UsernameToken>
    <wsse:Username>Username</wsse:Username>
    <wsse:Password>Password</wsse:Password>
  </wsse:UsernameToken>
</wsse:Security>

Constructing the WSSE headers

Adding headers in soap4r requires constructing an object that responds to a method that will return the headers to be added. A helper class provided for this is SOAP::Header::SimpleHandler. SimpleHandler allows us to respond with a hash that is converted into XML.

SimpleHandler also expects subclasses to call its initialize method with an enclosing tag. We can use xsd4r (the XML parser/constructor used by soap4r) to create the tag and define its namespace:

XSD::QName.new(NAMESPACE, 'Security')

Our call back method is a hash containing the username and password to be sent:

{"UsernameToken" => {"Username" => USERNAME, "Password" => PASSWORD}}

The entire class looks like this:

require 'soap/header/simplehandler'

class WsseAuthHeader < SOAP::Header::SimpleHandler
  NAMESPACE = 'http://schemas.xmlsoap.org/ws/2002/07/secext'
  USERNAME  = 'username'
  PASSWORD  = 'password'

  def initialize()
    super(XSD::QName.new(NAMESPACE, 'Security'))
  end

  def on_simple_outbound
    {"UsernameToken" => {"Username" => USERNAME, "Password" => PASSWORD}}
  end
end

Adding the WSSE headers

Adding the headers to a soap4r driver is quite easy (this has only been tested with a driver that has been auto-generated from a WSDL file).

require 'soap_driver.rb'
require 'wsse_authentication.rb'
d = Driver.new
...
d.headerhandler << WsseAuthHeader.new()