How to add wsse security header to soap request

@Dino,

Someone using apigee proxy, they are sending request in soap format without security header. Need to add security header like below and hit proxy endpoint with soap input request along with security token in header.

Target endpoint will authenticate the request based on the SOAP Security header

<soapenv:Header>
      <wsse:Security soapenv:mustUnderstand="111" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
         <wsse:UsernameToken wsu:Id="UsernameToken-459">
            <wsse:Username>username</wsse:Username>
            <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">password</wsse:Password>
         </wsse:UsernameToken>
      </wsse:Security>
   </soapenv:Header>
Solved Solved
1 5 16.4K
1 ACCEPTED SOLUTION

I can think of two ways to do it.

  1. use the XSLT policy. The advantages here: great flexibility in what you can do. And the XSLT policy is built-in to Apigee. Disadvantage: you need to write XSLT. (I'll leave that to you as an exercise for the reader)
  2. Use the Edit-XML-Node callout. Advantage: no XSLT. Disadvantage: It relies on an external JAR file. (There's even an example in the README for this callout that shows this)

View solution in original post

5 REPLIES 5

I can think of two ways to do it.

  1. use the XSLT policy. The advantages here: great flexibility in what you can do. And the XSLT policy is built-in to Apigee. Disadvantage: you need to write XSLT. (I'll leave that to you as an exercise for the reader)
  2. Use the Edit-XML-Node callout. Advantage: no XSLT. Disadvantage: It relies on an external JAR file. (There's even an example in the README for this callout that shows this)

This is the XSL that might do it for you.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
    xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
    xmlns:xslt="http://xml.apache.org/xslt"
    exclude-result-prefixes="xslt">




  <xsl:param name="uid" select="''"/>
  <xsl:param name="pwd" select="''"/>


  <xsl:output
      method="xml"
      omit-xml-declaration="yes"
      indent="yes"
      xslt:indent-amount="2"
      media-type="string"/>


  <xsl:template match="/soap:Envelope">
    <soap:Envelope>
      <soap:Header>
        <xsl:choose>
          <xsl:when test="soap:Header">
            <xsl:apply-templates select="soap:Header"/>
          </xsl:when>
          <xsl:otherwise>
            <wsse:Security>
              <xsl:call-template name="injectUsernameToken"/>
            </wsse:Security>
          </xsl:otherwise>
        </xsl:choose>
      </soap:Header>


      <soap:Body>
        <xsl:apply-templates select="soap:Body"/>
      </soap:Body>
    </soap:Envelope>
  </xsl:template>


  <xsl:template match="soap:Body">
    <xsl:copy-of select="*" />
  </xsl:template>


  <xsl:template match="soap:Header">
    <xsl:choose>
      <xsl:when test='wsse:Security'>
        <xsl:apply-templates select="*"/>
      </xsl:when>
      <xsl:otherwise>
        <wsse:Security>
          <xsl:call-template name="injectUsernameToken"/>
        </wsse:Security>
        <xsl:copy-of select="*" />
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>


  <xsl:template match="soap:Header/wsse:Security">
    <wsse:Security>
      <xsl:call-template name="injectUsernameToken"/>
      <xsl:copy-of select="*" />
    </wsse:Security>
  </xsl:template>


  <xsl:template match="soap:Header/wsse:Security/wsse:UsernameToken"/>


  <xsl:template name='injectUsernameToken'>
         <wsse:UsernameToken wsu:Id="UsernameToken-459">
            <wsse:Username><xsl:value-of select="$uid"/></wsse:Username>
            <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"><xsl:value-of select="$pwd"/></wsse:Password>
         </wsse:UsernameToken>
  </xsl:template>


</xsl:stylesheet>



i tried with your's xsl code and added AssignMessagePolicy before xsl transform flow for username and password and getting error like 
{
    "fault": {
        "faultstring": "Evaluation of XSL XSL-Transform.xsl failed with reason: \"Expected </soapenv:Envelope> at line 10(possibly  around char 415)\"",
        "detail": {
            "errorcode": "steps.xsl.XSLEvaluationFailed"
        }
    }
}

<br>av-mockgetusercreds.txterror.txtproxyinputrequest.txtxsltransform.txtxsl-transform.txt

The error is not with the XSL I sent to you. The error is: Your XML is not well formed.

This is what you attached.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
  </soap:Header>
  <soapenv:Body>
    <tem:GetCsrAccountLevel xmlns:tem="http://tempuri.org/">
      <tem:btn>7812780001292</tem:btn>
      <tem:secureId>XXXX</tem:secureId>
    </tem:GetCsrAccountLevel>
  </soapenv:Body>
</soap:Envelope> <!-- error: the soap prefix is not defined here -->

You have declared "soap" as a prefix on the Header element. But you're trying to use "soap" as a prefix on the ending tag of the Envelope element. The Envelope element is not a child of Header. Therefore "soap" as a prefix is unknown at that location.

You could fix this really easily by changing it to this:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
  </soap:Header>
  <soapenv:Body>
    <tem:GetCsrAccountLevel xmlns:tem="http://tempuri.org/">
      <tem:btn>7812780001292</tem:btn>
      <tem:secureId>XXXX</tem:secureId>
    </tem:GetCsrAccountLevel>
  </soapenv:Body>
</soapenv:Envelope> <!-- Well-formed, but still probably broken -->

That's well-formed XML... But it's probably broken. You are mixing soap namespaces for SOAP v1.1 and SOAP v1.2 in the same document.

For reference, these are the namespaces for the respective versions of SOAP:

soap versionnamespace URI
v1.1http://schemas.xmlsoap.org/soap/envelope/
v1.2http://www.w3.org/2003/05/soap-envelope

I have never seen a valid soap document (either a request or a response) that relies on both the soap v1.1 namespace and the soap v1.2 namespace. So you should consider carefully if that's what you really want. Probably you want just one of those.

Also if you're not clear on XML namespaces , which SOAP uses, then you may want to read up on the topic. This is a pretty good introduction.

I think you want to depend on ONE version of soap. And one prefix. Like this;

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header>
  </soap:Header>
  <soap:Body>
    <tem:GetCsrAccountLevel xmlns:tem="http://tempuri.org/">
      <tem:btn>7812780001292</tem:btn>
      <tem:secureId>XXXX</tem:secureId>
    </tem:GetCsrAccountLevel>
  </soap:Body>
</soap:Envelope> <!-- OK for soap v1.1 -->

The XSL that I showed uses soap v1.1. You could modify the XSL to use soap v1.2 very easily, by changing the namespace URI for the soap prefix.

And remember, the prefix itself is not semantically relevant. The important thing is the namespace URI. Therefore the above is equivalent to :

<ns1:Envelope xmlns:ns1="http://schemas.xmlsoap.org/soap/envelope/">
  <ns1:Header>
  </ns1:Header>
  <ns1:Body>
    <tem:GetCsrAccountLevel xmlns:tem="http://tempuri.org/">
      <tem:btn>7812780001292</tem:btn>
      <tem:secureId>XXXX</tem:secureId>
    </tem:GetCsrAccountLevel>
  </ns1:Body>
</ns1:Envelope> <!-- OK for soap v1.1 -->

And also remember that you don't absolutely need prefixes. You can rely on the default namespace for an element and its children, like this:

<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
  <!-- No Prefix. Uses default xmlns for soap v1.1 -->
  <Header>
  </Header>
  <Body>
    <tem:GetCsrAccountLevel xmlns:tem="http://tempuri.org/">
      <tem:btn>7812780001292</tem:btn>
      <tem:secureId>XXXX</tem:secureId>
    </tem:GetCsrAccountLevel>
  </Body>
</Envelope> 

And you can take it a step further using a default xmlns for the inner element too:

<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
  <!-- Uses default xmlns for soap v1.1 NS  -->
  <Header>
  </Header>
  <Body>
  <!-- Uses default xmlns for NS http://tempuri.org -->
    <GetCsrAccountLevel xmlns="http://tempuri.org/">
      <btn>7812780001292</btn>
      <secureId>XXXX</secureId>
    </GetCsrAccountLevel>
  </Body>
</Envelope>

The final four example XML documents are all semantically equivalent!

Thanks Dino, i got my mistake