I'm trying to consume a service from a webservice that asks for authentication, but I can not generate the Timestamp and UsernameToken.
<wsu:Timestamp wsu:Id="TS-1C1ABE5282FC96252314981531909334">
<wsse:UsernameToken wsu:Id="UsernameToken-1C1ABE5282FC96252314981531792593">
Send Corret:
<soapenv:Header>
<wsse:Security soapenv:mustUnderstand="1"
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">
<wsu:Timestamp wsu:Id="TS-1C1ABE5282FC96252314981531909334">
<wsu:Created>2017-07-20T22:07:01.999Z</wsu:Created>
<wsu:Expires>2017-07-20T22:10:01.999Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken wsu:Id="UsernameToken-1C1ABE5282FC96252314981531792593">
<wsse:Username>xxxxxxxxxxxxx</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">nrg2241zhN8HMAn1bg7OLCL/6eM=</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">ODgwODIzNDMz</wsse:Nonce>
<wsu:Created>2017-07-20T22:07:01.999Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
I'm using the function below:
/**
* This function implements a WS-Security authentication for PHP.
*
* #access private
* #param string $user
* #param string $password
* #return SoapHeader
*/
function soapClientWSSecurityHeader($user, $password)
{
// Creating date using yyyy-mm-ddThh:mm:ssZ format
$tm_created = gmdate('Y-m-d\TH:i:s\Z');
$tm_expires = gmdate('Y-m-d\TH:i:s\Z', gmdate('U') + 180); //only necessary if using the timestamp element
// Generating and encoding a random number
$simple_nonce = mt_rand();
$encoded_nonce = base64_encode($simple_nonce);
// Compiling WSS string
$passdigest = base64_encode(sha1($simple_nonce . $tm_created . $password, true));
// Initializing namespaces
$ns_wsse = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd';
$ns_wsu = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd';
$password_type = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText';
$encoding_type = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary';
// Creating WSS identification header using SimpleXML
$root = new SimpleXMLElement('<root/>');
$security = $root->addChild('wsse:Security', null, $ns_wsse);
//the timestamp element is not required by all servers
$timestamp = $security->addChild('wsu:Timestamp', null, $ns_wsu);
$timestamp->addAttribute('wsu:Id', 'Timestamp-28');
$timestamp->addChild('wsu:Created', $tm_created, $ns_wsu);
$timestamp->addChild('wsu:Expires', $tm_expires, $ns_wsu);
$usernameToken = $security->addChild('wsse:UsernameToken', null, $ns_wsse);
$usernameToken->addChild('wsse:Username', $user, $ns_wsse);
$usernameToken->addChild('wsse:Password', $password, $ns_wsse)->addAttribute('Type', $password_type);
$usernameToken->addChild('wsse:Nonce', $encoded_nonce, $ns_wsse)->addAttribute('EncodingType', $encoding_type);
$usernameToken->addChild('wsu:Created', $tm_created, $ns_wsu);
// Recovering XML value from that object
$root->registerXPathNamespace('wsse', $ns_wsse);
$full = $root->xpath('/root/wsse:Security');
$auth = $full[0]->asXML();
return new SoapHeader($ns_wsse, 'Security', new SoapVar($auth, XSD_ANYXML), true);
}
My return using the above function:
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
Id="Timestamp-28">
<wsu:Created>2017-07-20T22:18:53Z</wsu:Created>
<wsu:Expires>2017-07-20T22:21:53Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken>
<wsse:Username>XXXXXXXXXXXXXXXXXXX</wsse:Username>
<wsse:Password
Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">
XXXXXXXXXXXXXXXXXX
</wsse:Password>
<wsse:Nonce
EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">
OTUxOTA4NDYz
</wsse:Nonce>
<wsu:Created xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
2017-07-20T22:18:53Z
</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
These two tokens need to be generated, but with this function they are not.
You should try usine the WsSecurity project from Github that handles this sort of thing by facilitating the soap header construction and its inclusion on your soap request
Related
I am trying soap request with authentication. Wdsl server requiring user authentication. So i used SOAP header method.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://service.connector.uut.cs.com.tr/">
<soapenv:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken>
<wsse:Username>my_user_name</wsse:Username>
<wsse:Password>my_password</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
<soapenv:Body>
<ser:sendInvoice>
<id>123456</id>
<type>INVOICE</type>
<invoiceNo>AAA123456001</invoiceNo>
<data></data>
<hash>dfa06b85d66ef786f9f091f38e09c184</hash>
<mimeType>application/xml</mimeType>
<version>1.0</version>
</ser:sendInvoice>
</soapenv:Body>
</soapenv:Envelope>
Php soap request:
$auth = array(
'Username' => 'my_user_name',
'Password' => 'my_password',
);
$url = 'https://test.com/ws/connectorService?wsdl';
try {
$request = new SoapClient( $url, $auth );
$header = new SOAPHeader( $url, 'Security', $auth );
$request->__setSoapHeaders( $header );
$result = $request->sendInvoice();
} catch( Exception $exc ) {
$result = $exc->getMessage();
}
The $result is printing "java.lang.NullPointerException".
I need to add the following complex header with different namespace and types to my SoapClient Header.
<soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
<wsse:Security soapenv:mustUnderstand="1" 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>
<wsse:Username>****</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">*****</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
<wsa:Action>/IntS5/S5WS</wsa:Action>
</soapenv:Header>
As proposed in other answers I tried the following approach in my php project.Since I need WSA Addressing, I set the wsa:Action with security header together.
$header_part = '<wsse:Security soapenv:mustUnderstand="1" 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><wsse:Username>****</wsse:Username><wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">****</wsse:Password>'
. '</wsse:UsernameToken></wsse:Security><wsa:Action>/IntS5/S5WS</wsa:Action>';
$soap_var_header = new SoapVar($header_part, XSD_ANYXML, null, null, null);
$soap_header = new SOAPHeader('http://www.w3.org/2005/08/addressing', 'wsa', $soap_var_header);
$client->__setSoapHeaders($soap_header);
Some how __soapCall returns me the following error.
SoapFault exception: [Client] DTD are not supported by SOAP.I am not sure if it's related to header or the parameters of the putCall. Anybody can help me ?
EDIT: The problem is probably related with the namespace of the HEADER/Envelope.
The request which is sent to server looks like this.
UPDATED
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://ws.s5.mediasat.de/" xmlns:ns2="http://www.w3.org/2005/08/addressing">
<env:Header>
<wsse:Security env:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken>
<wsse:Username>****</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">*****</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
<wsa:Action xmlns:wsa="http://www.w3.org/2005/08/addressing">/IntS5/S5WS</wsa:Action>
</env:Header>
<env:Body>
<ns1:putCall>
<transaction>createIncident</transaction>
<transactionSender>Request</transactionSender>
<caseDataModel>
<senderId>7</senderId>
<ticketTypeId>102</ticketTypeId>
<title>test</title>
<priorityId>101</priorityId>
<categoryId>128</categoryId>
<description>Description</description>
<ticketNumberSender>INCC00000743809</ticketNumberSender>
<createTypeId>701</createTypeId>
<serviceId>B001APP05K</serviceId>
<categoryId>128</categoryId>
<serviceRecipientId>77888</serviceRecipientId>
<serviceLocationSAPCode>V135</serviceLocationSAPCode>
</caseDataModel>
</ns1:putCall>
</env:Body>
</env:Envelope>
The request that works on SOAP UI
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://ws.s5.mediasat.de/">
<soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
<wsse:Security soapenv:mustUnderstand="1" 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>
<wsse:Username>***</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">***</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
<wsa:Action>/IntS5/S5WS</wsa:Action>
</soapenv:Header>
<soapenv:Body>
<ws:putCall>
<transaction>createIncident</transaction>
<transactionSender>Request</transactionSender>
<caseDataModel>
<senderId>7</senderId>
<ticketTypeId>102</ticketTypeId>
<title>test</title>
<priorityId>101</priorityId>
<categoryId>128</categoryId>
<description>Description</description>
<ticketNumberSender>INCC00000743809</ticketNumberSender>
<createTypeId>701</createTypeId>
<serviceId>B001APP05K</serviceId>
<categoryId>128</categoryId>
<serviceRecipientId>77888</serviceRecipientId>
<serviceLocationSAPCode>V135</serviceLocationSAPCode>
</caseDataModel>
</ws:putCall>
</soapenv:Body>
</soapenv:Envelope>
Is there anyway to set/override the namespace for the header?
You should generate the header procedurally instead of using the string.
Example:
<?php
$client = new SoapClient(null, array(
'location' => "http://localhost/soap/",
'uri' => "http://ws.s5.mediasat.de/",
'soap_version' => SOAP_1_2
));
$username = '***';
$password = '***';
$sec_namespace = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd';
$wsa_namespace = 'http://www.w3.org/2005/08/addressing';
// prepare security token
$node1 = new \SoapVar($username, XSD_STRING, null, null, 'Username', $sec_namespace);
$xml = new XMLWriter(); // this is a little hacky, but no other way to set the type as far as I know
$xml->openMemory();
$xml->startElementNS('wsse', 'Password',$sec_namespace);
$xml->writeAttribute('Type', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText');
$xml->Text($password);
$xml->endElement();
$node2 = new \SoapVar($xml->outputMemory(), XSD_ANYXML);
$token = new \SoapVar(array($node1, $node2), SOAP_ENC_OBJECT, null, null, 'UsernameToken', $sec_namespace);
$security = new \SoapVar(array($token), SOAP_ENC_OBJECT, null, null, 'Security', $sec_namespace);
$soap_header_sec = new \SOAPHeader($sec_namespace, 'Security', $security, true);
// prepare action token
$action = new \SoapVar('/IntS5/S5WS', XSD_STRING, null, null, 'Action', $wsa_namespace);
$soap_header_action = new \SOAPHeader($wsa_namespace, 'Action', $action, false);
// set prepared headers
$client->__setSoapHeaders(
[$soap_header_sec,$soap_header_action]
);
$client->putCall();
This will produce the valid envelope with all namespaces set:
<?xml version="1.0" encoding="utf-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://ws.s5.mediasat.de/" xmlns:ns2="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:ns3="http://www.w3.org/2005/08/addressing" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:enc="http://www.w3.org/2003/05/soap-encoding">
<env:Header>
<ns2:Security env:mustUnderstand="true">
<ns2:UsernameToken>
<ns2:Username>***</ns2:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">***</wsse:Password>
</ns2:UsernameToken>
</ns2:Security>
<ns3:Action>/IntS5/S5WS</ns3:Action>
</env:Header>
<env:Body>
<ns1:putCall env:encodingStyle="http://www.w3.org/2003/05/soap-encoding" />
</env:Body>
</env:Envelope>
The name space "http://www.w3.org/2005/08/addressing" is not bound to the prefix "wsa".
The "mustUnderstand" attribute must use the prefix "env" not "soapenv".
You can declare the missing namespace locally. You just have to fix you header_part to look like this:
header_part = '<wsse:Security env:mustUnderstand="1" 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><wsse:Username>****</wsse:Username><wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">****</wsse:Password>'
. '</wsse:UsernameToken></wsse:Security><wsa:Action xmlns:wsa="http://www.w3.org/2005/08/addressing">/IntS5/S5WS</wsa:Action>';
Note: Maybe it is obvious but it is kind of dangerous to assume that the prefix for the "http://www.w3.org/2003/05/soap-envelope" will always be "env". The framework can change this at any time. If you want to be on the really safe side you might prefer to bind the namespace locally to a new prefix:
<wsse:Security soapenv:mustUnderstand="1" xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope" ...
But having one namespace bound to two different prefixes will make things even harder to read and understand so I would say it is a question of taste.
I am facing an issue with SOAP Header in PHP
My code should generate something like this
<soapenv:Header>
<ns1:RequestHeader xmlns:ns1="xxxxxxx">
<ns1:auth xmlns:ns1="yyyy">
<ns1:user>xxx</ns1:user>
<ns1:pass>false</ns1:pass>
</ns1:auth>
</ns1:RequestHeader>
</soapenv:Header>
The issue, I can't figure out how to do it correctly and I found many tricks to do it but most of them will force me either to change how I send the main XML or change my entire code.
My current code print the following
<soapenv:Header>
<ns1:RequestHeader>
<ns1:auth>
<ns1:user>xxx</ns1:user>
<ns1:pass>false</ns1:pass>
</ns1:auth>
</ns1:RequestHeader>
</soapenv:Header>
and my code is
$Auth = new stdClass();
$Auth->auth->user = "user";
$Auth->auth->pass = "pass";
$header = new SoapHeader(true,'RequestHeader',$Auth,false);
$client->__setSoapHeaders($header);
What do you think about this, how can I add Attributes correctrly to RequestHeader?
Thanks
I found the solution for that,
$header_part = '
<wsse:Security xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext" SOAP-ENV:mustUnderstand="1">
<wsse:UsernameToken>
<wsse:Username>'."USERNAME".'</wsse:Username>
<wsse:Password>'."PASSWORD".'</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
';
$soap_var_header = new SoapVar( $header_part, XSD_ANYXML, null, null, null );
$soap_header = new SoapHeader( 'http://your-target-service.com', 'wsse', $soap_var_header );
$soap_client->__setSoapHeaders($soap_header);
you need to send this with the original soap request.
i need use a SOAP webservice for a project.
The webservice requires authentication via a SOAP header like this:
<soap:Header>
<wsse:Security soap:mustUnderstand="true" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken>
<wsse:Username>STAGING_WS</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">ANDREANI</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>
So, i tried build the headers using SoapHeader class as below:
$ns = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
$auth = new \StdClass();
$auth->Username = new \SoapVar('STAGING_WS', XSD_STRING, NULL, $ns, NULL, $ns);
$auth->Password = new \SoapVar('ANDREANI', XSD_STRING, NULL, $ns, NULL, $ns);
$token = new \StdClass();
$token->UsernameToken = new \SoapVar($auth, SOAP_ENC_OBJECT, NULL, $ns, 'UsernameToken', $ns);
$security = new \SoapVar(new \SoapVar($token, SOAP_ENC_OBJECT, NULL, $ns, 'UsernameToken', $ns), SOAP_ENC_OBJECT, NULL, $ns, 'Security', $ns);
$header = new \SoapHeader('wsse', 'Security', $security, true);
But, the generatd header is not equal:
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://www.andreani.com.ar" xmlns:ns2="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:ns3="wsse">
<env:Header>
<ns3:Security env:mustUnderstand="true">
<ns2:UsernameToken>
<ns2:Username>STAGING_WS</ns2:Username>
<ns2:Password>ANDREANI</ns2:Password>
</ns2:UsernameToken>
</ns3:Security>
</env:Header>
<env:Body>
<ns1:ConfirmarCompra/>
</env:Body>
</env:Envelope>
Any ideas ?
Try changing the last line to:
$header = new \SoapHeader($ns, 'Security', $security, true);
This will make the namespace of the Security tag to be the same as UsernameToken, Username and Password tags. The local name for the security namespace will be still ns2 and not wsse but that doesn't really matter. The correct SOAP Server must accept it as long as the namespace url is correct.
I want to create a WSS header to authentificate on secured web services.
I can do it using an ugly :
$auth = '
<wsse:Security SOAP-ENV:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsu:Timestamp wsu:Id="Timestamp-28" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsu:Created>' . $timestamp . '</wsu:Created>
<wsu:Expires>' . $timestampExpires . '</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken wsu:Id="UsernameToken-27" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:Username>' . $user . '</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">' . $passdigest . '</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">' . $encodedNonce . '</wsse:Nonce>
<wsu:Created>' . $timestamp . '</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>';
I am now trying to do it cleaner, using SimpleXML.
But if I try to do a simple :
$xml = new SimpleXMLElement('<wsse:Security/>', 0, false, 'wsse');
I get :
warning: SimpleXMLElement::__construct() [simplexmlelement.--construct]:
namespace error : Namespace prefix wsse on Security is not defined in
I think I miss something with the way to create namespaced xmls, can you give me some hints?
I found a way to solve my problem :
$root = new SimpleXMLElement('<root/>');
$security = $root->addChild('wsse:Security', 'test', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd');
$root->registerXPathNamespace('wsse', 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd');
$auth = $root->xpath('/root/wsse:Security');
echo htmlentities($auth[0]->asXML());
Displays :
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">test</wsse:Security>
And also, there is a mistake in my XML, I put a SOAP-ENV:mustUnderstand="1" but I never define the SOAP-ENV namespace.