Applying WS-S UsernameToken and WS-Security

Hi,

I have a requirement.  Currently we are using Broadcomm layer7, I had implemented adding WS-S UsernameToken and WS-Security. Now we are planning to move all proxies from Layer7 to Apigee. 

on Layer7, once SOAP Payload reaches proxy, we are adding plain username/password  and then applying ws-security to the request payload and sending it to backend. here is the screenshot from layer7. I am trying to download zip file from previous accepted solution but getting 403 error.

https://www.googlecloudcommunity.com/gc/Apigee/How-to-add-WS-Security-Username-Token-for-a-SOAP-requ...

on Layer7, it is simple and we just need to drop Assertions. Can you please help me on how can we achieve same requirement in APIGEE.

 

rajkalgur_0-1702166608659.png

 

rajkalgur_0-1702167556871.png

 

 

 

Solved Solved
0 16 879
3 ACCEPTED SOLUTIONS

Ahh, yes. That document is SOAP 1.2.  The callout from 2023-12-11 supports only soap 1.1.  I've updated it so that it now supports soap1.1 and soap1.2.  Please pull the latest and retry?

View solution in original post

If you are using the sample proxy bundle included in that repository as your starting point, then this is an easy one. You're going to headslap.😣

If you look at the proxy endpoint configuration, for the flows for /inject1 and /inject2 ... those flows call the custom Java callout policy to inject the UsernameToken and set the variable called output , in the Response flow. In retrospect, it was probably a mistake for me to attach the java policy there, for the example, but with no target, it didn't matter. 

But, If you add a target to that proxy bundle and try to access the output variable in the target Request preflow, which is obviously prior to the Response flow on the proxy endpoint, the output variable hasn't been set yet, so it will be empty.

If you're going to use this Java policy (Java-WSSEC-Inject-UsernameToken-with-Plaintext-Password) you probably want it to be attached in the target request preflow, just prior to your AssignMessage to set the payload. I'll update the sample to inject the username token in the proxy request flow.... so in the future, other people won't trip over this.

Also , you can avoid the AssignMessage completely, by substituting message.content for the output variable in that custom java policy. 

 

<JavaCallout name='Java-WSSEC-Inject-UsernameToken-with-Password-Digest'>
  <Properties>
    <Property name='source'>message.content</Property>
    <!-- output this right into the message content, no need for an interim variable -->
    <Property name='output-variable'>message.content</Property>
    <Property name='username'>emil@gaffanon.com</Property>
    <Property name='password'>my_secret_password!</Property>
    <Property name='password-encoding'>DIGEST</Property>
  </Properties>
  <ClassName>com.google.apigee.callouts.wssecusernametoken.Inject</ClassName>
  <ResourceURL>java://apigee-wssecusernametoken-20231212.jar</ResourceURL>
</JavaCallout>

 

View solution in original post

Thank you. Can you please add timestamp and provide latest jar. 

View solution in original post

16 REPLIES 16


@rajkalgur wrote:

we are adding plain username/password  and then applying ws-security to the request payload and sending it to backend. here is the screenshot from layer7


Hi Raj

At one point you said you want to "apply WS-Security" and at another point you said  "we just need to drop Assertions."  The latter sounds like "removing WS-Security".  So I'm not clear on exactly what the obstacle is. 

I understand what your'e saying regarding the SOAP message and adding a username token.  In Apigee, thereis no "builtin" policy step type that does that work, though there is a "community contributed" callout that does this. https://github.com/DinoChiesa/Apigee-Java-WsSec-Username-Token This Java callout does what I think you described - inserts a username token that complies with WS-Security standard into a SOAP message.  You can use it just like a builtin policy, but you need to add the Java JAR to the API proxy. The README for that repo describes it in more detail and there's an example API Proxy that shows you how to do it. 

about the 403 - I guess you were trying to download the example proxy showing the other option, which was to use JavaScript + XSL to do the same thing?  I just tried that and also got the 403 Forbidden.  Not sure what is going on there.   Attached here please find a reproduction of that example proxy (the one that uses JavaScript and XSL).  If I use this one, it gives me these results: 

curl -i -X POST $apigee/add-wssec-username-token/t1 -H content-type:text/xml -d '
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
  <Body>
    <GetProxy xmlns="">
      <ProxiesList/>
    </GetProxy>
  </Body>
</Envelope>
'

HTTP/2 200 
user-agent: curl/8.3.0
accept: */*
content-type: text/xml
content-length: 935
x-cloud-trace-context: 77d3a45629bf29548394f1f71dd1b42b/8169754887577606502
x-forwarded-for: 104.132.51.83, 34.111.78.193,10.0.15.202
x-forwarded-proto: https
x-request-id: cb6c4a44-a7a2-4acb-9c45-5cca4d4ff517
x-b3-traceid: 98b4f2ed7ea99e345470614387dcc5d0
x-b3-spanid: 5470614387dcc5d0
x-b3-sampled: 0
apiproxy: add-wssec-username-token r9
date: Tue, 12 Dec 2023 03:12:00 GMT
via: 1.1 google, 1.1 google
alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000

<soap:Envelope 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">
   <soap:Header>
      <wsse:Security>
         <wsse:UsernameToken wsu:Id="UsernameToken-459">
            <wsse:Username>Alfred001</wsse:Username>
            <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">F9gUr02Ashc9UUoWgAWjoSSoKJ0=</wsse:Password>
            <wsse:Nonce>2a0845ae-1448-4053-b6bf-04e420ed12d08</wsse:Nonce>
            <wsse:Created>2023-12-12T03:12:00.509Z</wsse:Created>
         </wsse:UsernameToken>
      </wsse:Security>
   </soap:Header>
   <soap:Body>
      <GetProxy>
         <ProxiesList/>
      </GetProxy>
   </soap:Body>
</soap:Envelope>

also I saw you added a question to that other thread. I'll answer there and point to this Q&A. 

 

Hi Dino,

sorry for the confusion. I gone through the implementation and applied same on APIGEE X with no target backend. here is the response i am getting. 

 

rajkalgur_2-1702332217855.png

 

rajkalgur_1-1702332137326.png

 

 

 

i tried from SOUPUI, i got 200 response but nothing is added to the response payload.

rajkalgur_0-1702339017321.png

 

I just tested this  myself and it works for me. Just to clarify, there are TWO options we are discussing here, regarding injecting a UsernameToken into a SOAP message.  One which uses JavaScript + XSL, and which is attached in my prior reply.  The other is the Java callout, which I think you are asking about above. 

regarding "ExecutionFailed".  I can only guess as to what the problem is. "ExecutionFailed" usually means there is a problem with the Java JAR.  Your API Proxy should have a resources/java directory and there should be a jar within it.  It should look something like this:

 

apiproxy/
apiproxy/wssec-username.xml
apiproxy/resources/
apiproxy/resources/java/
apiproxy/resources/java/apigee-wssecusernametoken-20231211.jar
apiproxy/policies/
apiproxy/policies/AM-Response.xml
apiproxy/policies/Java-WSSEC-Inject-UsernameToken-with-Password-Digest.xml
apiproxy/policies/Java-WSSEC-Inject-UsernameToken-with-Plaintext-Password.xml
apiproxy/policies/RF-Invalid-Request.xml
apiproxy/policies/RF-Unknown-Request.xml
apiproxy/policies/AM-Clean-Request-Headers-From-Response.xml
apiproxy/policies/AM-Inject-Proxy-Revision-Header.xml
apiproxy/proxies/
apiproxy/proxies/endpoint1.xml

 

(that is updated, see my note below. but the older version of the example proxy should look roughly similar)

That JAR defines the custom class that does the UsernameToken injection into the soap message. If that jar is missing you may get the problem you are seeing.

I've updated the callout to support password digests. If you want that you will want to pull the repo again. 

Here's an example of my run in Apigee X:

 

$ curl -i $apigee/wssec-username/inject2  -H content-type:application/xml \
       --data-binary @./sample-data/request1.xml

HTTP/2 200 
content-type: application/xml
apiproxy: bundle r2
x-request-id: 16833b22-1e07-4635-ae32-cfd9e1a1460e
content-length: 1060
date: Tue, 12 Dec 2023 03:08:58 GMT
via: 1.1 google
alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000


<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wssec="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:ns1="http://ws.example.com/">
  <soapenv:Header><wssec:Security soapenv:mustUnderstand="1"><wssec:UsernameToken wsu:Id="UsernameToken-a85aad02-9c3a-4295-ab6c-4ec847447a66"><wssec:Username>emil@gaffanon.com</wssec:Username><wssec:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">fMucebvxfXUL0XhPVPz64mB21/Y=</wssec:Password><wsu:Created>2023-12-12T03:08:58Z</wsu:Created><wssec:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">bJDbBkrvA8mNYbJxnw/WfiC4HDI=</wssec:Nonce></wssec:UsernameToken></wssec:Security></soapenv:Header><soapenv:Body>
    <ns1:sumResponse>
      <ns1:return>9</ns1:return>
    </ns1:sumResponse>
  </soapenv:Body>
</soapenv:Envelope>

 

Hi Dino,

it is working now when i use the payload which is in sample-data folder. will it work for any soap payload?

I am getting invalid request error when i use below payload

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:ns="http://xmlns.tmw.com/Schemas/Common/header/1.0.0" xmlns:v1="http://www.tmw.com/schemas/storeops/tux/reservation/v1">
<soap:Header>
<ns:Header>
<ns:Source>Web</ns:Source>
<ns:Domain>STOREOPS</ns:Domain>
<ns:ReferenceID></ns:ReferenceID>
<ns:UserID>DFGH18</ns:UserID>
<ns:Password>TEST123</ns:Password>
<ns:CompanyID>TMWNCA</ns:CompanyID>
<ns:Version>1.0</ns:Version>
<ns:IPAddress>173.45.6712.145</ns:IPAddress>
</ns:Header>
</soap:Header>
<soap:Body>
<v1:GetReservationRequest>
<v1:ID>115097597</v1:ID>
</v1:GetReservationRequest>
</soap:Body>
</soap:Envelope>

Ahh, yes. That document is SOAP 1.2.  The callout from 2023-12-11 supports only soap 1.1.  I've updated it so that it now supports soap1.1 and soap1.2.  Please pull the latest and retry?

Thank you Dino. I updated the jar. its working now.  I am trying to send new updated payload with ws security to real backend target server but it throwing 500 errors.

I have created new AM policy and attached to target endpoint request preflow.

 

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<AssignMessage continueOnError="false" enabled="true" name="AM-append-wssecurity">
<DisplayName>AM-append-wssecurity</DisplayName>
<Properties/>
<Set>
<Payload contentType="application/xml">
{output}</Payload>
</Set>
<AssignTo createNew="true" transport="http" type="request"/>
<IgnoreUnresolvedVariables>true</IgnoreUnresolvedVariables>
</AssignMessage>
 
under payload section output variable data is coming as blank. 
 
rajkalgur_0-1702415710042.png

 

rajkalgur_1-1702415790759.png

 

If you are using the sample proxy bundle included in that repository as your starting point, then this is an easy one. You're going to headslap.😣

If you look at the proxy endpoint configuration, for the flows for /inject1 and /inject2 ... those flows call the custom Java callout policy to inject the UsernameToken and set the variable called output , in the Response flow. In retrospect, it was probably a mistake for me to attach the java policy there, for the example, but with no target, it didn't matter. 

But, If you add a target to that proxy bundle and try to access the output variable in the target Request preflow, which is obviously prior to the Response flow on the proxy endpoint, the output variable hasn't been set yet, so it will be empty.

If you're going to use this Java policy (Java-WSSEC-Inject-UsernameToken-with-Plaintext-Password) you probably want it to be attached in the target request preflow, just prior to your AssignMessage to set the payload. I'll update the sample to inject the username token in the proxy request flow.... so in the future, other people won't trip over this.

Also , you can avoid the AssignMessage completely, by substituting message.content for the output variable in that custom java policy. 

 

<JavaCallout name='Java-WSSEC-Inject-UsernameToken-with-Password-Digest'>
  <Properties>
    <Property name='source'>message.content</Property>
    <!-- output this right into the message content, no need for an interim variable -->
    <Property name='output-variable'>message.content</Property>
    <Property name='username'>emil@gaffanon.com</Property>
    <Property name='password'>my_secret_password!</Property>
    <Property name='password-encoding'>DIGEST</Property>
  </Properties>
  <ClassName>com.google.apigee.callouts.wssecusernametoken.Inject</ClassName>
  <ResourceURL>java://apigee-wssecusernametoken-20231212.jar</ResourceURL>
</JavaCallout>

 

Hi Dino,

let me change and will update the outcome..

Hi Dino,

here is the error i am getting when hitting target endpoint.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<soapenv:Fault xmlns:axis2ns4699="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<faultcode>axis2ns4699:FailedAuthentication</faultcode>
<faultstring>security.wssecurity.WSSContextImpl.s02: com.ibm.websphere.security.WSSecurityException: Exception org.apache.axis2.AxisFault: CWWSS6521E: The Login failed because of an exception: javax.security.auth.login.LoginException: http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest was supplied, but only http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText is supported. ocurred while running action: com.ibm.ws.wssecurity.handler.WSSecurityConsumerHandler$1@360be401</faultstring>
<detail/>
</soapenv:Fault>
</soapenv:Body>
</soapenv:Envelope>

 

 

As you know, I am migrating policies from broadcomm layer7 to APIGEE X. here is the difference between payloads after adding WSSecurityusertoken.

Layer7:

rajkalgur_0-1702446573416.png

APIGEE X:

rajkalgur_1-1702446640129.png

 

Hi Dino,

I used plaintext password policy instead digest one. looks like i am sending correct payload to backend target server. I will test more and let you know the results.

Hi Dino,

So far testing is good but we have to test all operations which are supported by backend. Thanks for the wonderful solution 🙂

I accepted the solution.

a few comments regarding the differences .

  • the big thing is the Timestamp. The UsernameToken callout does not inject a Timestamp into the SOAP document. I could extend the callout to do that (it would be a small change), but today, the callout does not inject that element. Let me know if you agree that this Timestamp is essential. (Edit: I've updated the callout so that it can now optionally inject a Timestamp element. Get version 20231213 of the callout or later for this feature. Check the README for how to use it.)
  • There is a minor thing; a wsu:Id attached to the UsernameToken generated by the Apigee callout, where there is no such attribute in the UsernameToken generated by Layer7. That should be benign, no impact on you.
  • The PasswordText vs PasswordDigest ... that is a matter of how you've configured the Apigee callout . I see from your other reply that you seem to be aware of that.

Otherwise it looks pretty good. Let me know how your tests work out.

Thank you. Can you please add timestamp and provide latest jar. 

Thank you. I took the latest jar.