I am trying to consume a SOAP service using node-soap using WSSecurityCert
. The service requires me to set the timestamp in such a way that the SOAP request has a validity of 5 minutes and wsu
prefix. The node-soap
library has 10 minutes of validity "hard-coded", with no obvious way to override it. I don't know how or if I can just modify the timestamp before it is sent, because the WSSecurityCert
signature might be invalidated.
My code:
const client = await soap.createClientAsync(url);
const securityOptions = {
hasTimeStamp: true,
}
const wsSecurity = new soap.WSSecurityCert(PRIVATE_KEY, PUBLIC_CERT, '', securityOptions);
client.setSecurity(wsSecurity);
const result = await client.method(args);
The generated timestamp looks like this:
<Timestamp
xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
Id="_1">
<Created>2024-05-08T13:20:09Z</Created>
<Expires>2024-05-08T13:30:09Z</Expires>
</Timestamp>
I need to make the timestamp look something like this:
<wsu:Timestamp wsu:Id="TS-7C14BF4AA3E26845E015637928928701">
<wsu:Created>2024-05-08T13:20:09Z</wsu:Created>
<wsu:Expires>2024-05-08T13:25:09Z</wsu:Expires>
</wsu:Timestamp>
I tried to add created
and expires
among other things to securityOptions
, to no avail.
Is it possible to achieve this with the node-soap library without forking it?
soap
doesn't offer any way to customize the timestamp header, see the hardcoded lines: https://github.com/vpulim/node-soap/blob/master/src/security/WSSecurityCert.ts#L124.
But you can disable the library's timestamp generation and add your own.
Step 1: Disable hasTimestamp, add signing reference
const options: IWSSecurityCertOptions = {
hasTimeStamp: false,
additionalReferences: [
'wsu:Timestamp',
'wsa:To',
],
signerOptions: {
prefix: 'ds',
attrs: { Id: 'Signature' },
existingPrefixes: {
wsse11: 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd',
},
},
};
const wsSecurity = new WSSecurityCert(privateKey, publicKey, password, options);
soapClient.setSecurity(wsSecurity);
Step 2: Add your own timestamp
const expiry = '2100-05-08T00:00:00Z'; // TODO: compute this
const id = 'TS-7C14BF4AA3E26845E015637928928701'; // TODO: compute this
soapClient.addSoapHeader((methodName, location, soapAction, args) => {
return '<wsse:Security 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:mustUnderstand="1">' +
`<wsu:Timestamp wsu:Id="${id}">` +
`<wsu:Created>${new Date().toISOString()}</wsu:Created>` +
`<wsu:Expires>${expiry}</wsu:Expires>` +
'</wsu:Timestamp>' +
'</wsse:Security>';
});
Note that toISOString() will have the timestamp with miliseconds, this wasn't an issue for the SOAP service I'm working with.
Notice that the security header is included in here. This works because there is logic within soap
to adjust accordingly, see https://github.com/vpulim/node-soap/blob/master/src/security/WSSecurityCert.ts#L141.
Side note: I had to write my import statement like the following for the security classes, not sure if there's a better way:
import { IWSSecurityCertOptions, WSSecurityCert } from 'soap/lib/security/WSSecurityCert';
RESULT
Your SOAP security header will have:
<wsu:Timestamp wsu:Id="TS-7C14BF4AA3E26845E015637928928701">
<wsu:Created>
2024-05-10T17:23:56.937Z
</wsu:Created>
<wsu:Expires>
2100-05-08T00:00:00Z
</wsu:Expires>
</wsu:Timestamp>
And a signed reference like:
<ds:Reference URI="#TS-7C14BF4AA3E26845E015637928928701">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
<ds:DigestValue>
GR__REDACTED__e24=
</ds:DigestValue>
</ds:Reference>