I'm trying to use a webform to create incidents in ServiceNow. The ServiceNow API uses basic authentication with username, password and an apikey.
The authentication always fails as the apikey isn't recognized correctly. When using a SOAPClient like Boomerang it works, but I just don't get it correctly in php.
The following information are in the header in Boomerang (which works perfectly fine):
Authorization - Basic SU5UU0RQTVI6TWFudWZhY3R1cmluZ1czYmYwcm0=
Content-Type - text/xml; charset=utf-8
SOAPAction - http://www.service-now.com/hlr_incident_api/createIncident
Api-Key - 97c97bd4-5226-43c4-9909-adee8332d1e0
The code that I'm using (which always fails) looks like this:
$wsdl ='sns.wsdl';
$client = new SoapClient($wsdl, array('login' => 'USER',
'password' => 'PASSWORD',
'soap_version' => SOAP_1_2));
$ns = 'http://www.service-now.com/hlr_incident_api';
$header = new SoapHeader($ns, '97c97bd4-5226-43c4-9909-adee8332d1e0');
$client->__setSoapHeaders($header);
Can you help me setting up the correct SOAP header in php?
Thank you very much!!
Related
I'm currently developing a payment gateway that has to send the order to Navision where it will be managed. In the webservice the NTML authentication is enabled so first it is necessary to extend the native class SoapClient. For it I have found enough documentation in the web https://thomas.rabaix.net/articles/using-soap-php-with-ntlm-authentication that allows to extend this native class.
Now the code exposed in that post does not return me the xslm first.
In this case this would be my code
define("USERPWD", "user:password");
require_once("NTLMStream.php");
require_once("NTLMSoapClient.php");
stream_wrapper_unregister('http');
stream_wrapper_register('http', 'NTLMStream') or die("Failed to register protocol");
// Initialize Soap Client
$url = "http://ipaddress:port/DynamicsNAV1_test/WS/enterprise/Codeunit/SalesEnterprise?WSDL";
$uri = "urn:microsoft-dynamics-schemas/codeunit/SalesEnterprise";
$params = [
'stream_context' => stream_context_create([
'ssl' => [
'verify_peer'=>false,
'verify_peer_name'=>false,
'allow_self_signed'=>true,
]]),
'cache_wsdl'=> WSDL_CACHE_NONE,
'trace' => 1,
];
$client = new NTLMSoapClient($url, $params);
stream_wrapper_restore('http');
As you can see I have dispensed with the classes used by this author to define the credentials.
inally the code returns the following error:
SOAP Fault: (faultcode: WSDL, faultstring: SOAP-ERROR: Parsing WSDL: Couldn't load from 'http://ipaddress:port/DynamicsNAV1_test/WS/Enterprise/Codeunit/SalesEnterprise?WSDL' : Document is empty).
I will be happy to provide more information if needed. Many thanks in advance!
The first 2 responses you get from an NTLM handshake are 401's with no body, the 3rd response if successfully authenticated will contain the 200 and response.
https://techcommunity.microsoft.com/t5/iis-support-blog/windows-authentication-http-request-flow-in-iis/ba-p/324645
Update 30/1/21
Its been a while since ive used Php, but id start with this post that shows how to manually do the NTLM curl from the shell then how to repeat that same command from php curl - curl with ntlm authentication works in command line but not inside php
Also there are some nuggets in the top few answers of this SO search: https://stackoverflow.com/search?q=NTLM+PHP+curl
I had this issue with the same SOAP exception, but the code similar to yours was working fine with NAV2013. The company I'm working for upgraded to NAV2020 and then the SOAP Exception appeared. After countless hours of back and forward of refactoring, and researching the web, the solution is on the Microsoft documentation... The issue is that in the original code for NTLMSoapClient class you have to change the following line:
'Content-Type: text/xml; charset=UTF-8'
Which is under the variable $headers in the method processRequest to:
'Content-Type: text/xml; charset=ISO-8859-1'
At some point Dynamics API is no longer accepting credentials with Unicode characters. I hope this works for you as it did for me.
I need to use SOAP to retrieve some data from a database. I'm not an experienced PHP programmer, that's why I need some help. The company which provides the webservice (WSDL) gave me login info and links to the svc and wsdl files. They also gave me an example in C# of how to connect:
var proxy = new ChannelFactory<ServiceReferenceWCF.IWebService2>("custom");
proxy.Credentials.UserName.UserName = login;
proxy.Credentials.UserName.Password = pass;
var result = proxy.CreateChannel();
var logged_in = result.loggedIn();
Here's my PHP code:
$wsdl_proto = 'https';
$wsdl_host = 'their_wsdl_host';
$wsdl_host_path = 'their_wsdl_path';
$namespace_proto = 'https';
$namespace_host = 'their_namespace_host';
$namespace_path = 'their_namespace_path';
$location = $namespace_proto.'://'.$namespace_host.$namespace_path;
$wsdl_url = $wsdl_proto.'://'.$wsdl_host.$wsdl_host_path;
$connection = new SoapClient($wsdl_url, array('location' => $location, 'soap_version' => SOAP_1_1, 'connection_timeout'=> 600,
'proxy_login' => "my_login", 'proxy_password' => "my_password"));
$functions = $connection->__getFunctions();
var_dump($functions);
$logged_in = $connection->loggedIn();
It hangs during the loggedIn() function call. This function is listed in the $functions variable, so it is valid. I tried some other functions provided by the service - the result is always the same: the script simply freezes. And by that I mean there is no response from the service and PHP waits for the loggedIn() function to finish. After it exceeds the timeout, I get an error: Error Fetching http headers in...
What am I doing wrong? How can I debug it?
UPDATE:
I tried every single thing you guys suggested. But I still didn't manage to solve the problem. I don't use a proxy. You can find the results below:
1. I installed the SoapUI. After configuring the request for the some_method function (creating a basic Auth with credentials) I received a response: An error occurred when verifying security for the message.
Ticking the Authenticate pre-emptively option didn't help. I searched for a solution to this error, but I didn't find anything.
2. I tried almost every imaginable combination of options for the SoapClient class. Here are some of them:
$connection = new SoapClient($wsdl_url, array(
'login' => "login",
'password' => "pass",
'trace' => 1,
));
The response headers are empty. The request headers:
REQUEST HEADERS:
POST /file.svc HTTP/1.1
Host: host
Connection: Keep-Alive
User-Agent: PHP-SOAP/5.6.16
Content-Type: text/xml; charset=utf-8
SOAPAction: "http://tempuri.org/file/some_method"
Content-Length: 221
Authorization: Basic HASH
REQUEST:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://tempuri.org/"><SOAP-ENV:Body><ns1:some_method/></SOAP-ENV:Body></SOAP-ENV:Envelope>
Next combination:
$connection = new SoapClient($wsdl,array(
'login' => "login",
'password' => "pass",
'trace' => 1,
'connection_timeout' => 500000,
'cache_wsdl' => WSDL_CACHE_BOTH,
'keep_alive' => false,
));
The response headers are empty. The request headers are the same, as before.
3. Using this:
$connection->__setLocation('https://host.org/file.svc');
doesn't help. However when I set the location to the WSDL file instead of the SVC file, I get the following response:
HTTP/1.1 405 Method Not Allowed
Connection: Keep-Alive
Content-Length: 1293
Date: Wed, 23 Dec 2015 14:28:53 GMT
Content-Type: text/html
Server: Microsoft-IIS/7.5
Allow: GET, HEAD, OPTIONS, TRACE
X-Powered-By: ASP.NET
I'm sure that the WSDL service is not slow enough, to exceed the timeout (Ricardo Velhote suggested it).
4. I've got an XML configuration file provided with the C# example I mentioned earlier:
<client>
<endpoint address="https://host/file.svc" binding="customBinding" bindingConfiguration="custom" contract="ServiceReference1.file" name="custom" />
</client>
<bindings>
<customBinding>
<binding name="custom">
<security defaultAlgorithmSuite="Default" authenticationMode="UserNameOverTransport" requireDerivedKeys="true" includeTimestamp="true" messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10">
<localClientSettings detectReplays="false" />
<localServiceSettings detectReplays="false" />
</security>
<textMessageEncoding messageVersion="Soap11WSAddressing10" />
<httpsTransport maxReceivedMessageSize="2147483647" maxBufferPoolSize="2147483647" />
</binding>
</customBinding>
</bindings>
I tried to extend the SoapClient class as suggested here, but as you can guess it didn't work - the behaviour of the script is still the same.
According to your update and the XML configuration file provided with the example this Webservice appears to be using WS-Addressing so the regular SOAPClient will not work and requires it to be extended in order to support WS-Addressing.
This is the line that gives it away
<textMessageEncoding messageVersion="Soap11WSAddressing10" />
I have yet to use WS-Addressing but I was recently analysing an API for project we will be working on in the future that requires it.
Please take note of this project or this other project which may be useful (it's easy to search for more PHP WS-Addressing).
Again, I have only done research and do not have any hands-on experience to help you with actual code :)
[EDIT: Obsolete answer after the update]
First of all, you might be being mislead by the use of the variable proxy in the example code. They are probably referring to HTTP Basic Authentication and not a proxy.
Try replacing proxy_login and proxy_password with login and password.
However, having said that, if you are getting the WSDL it means that at least it's connecting and obtaining the information about the service (which is good).
In normal situations you do not need to specify location in SoapClient as it should be defined by the WSDL file. By setting the location parameter you are overriding what is set in the WSDL file and you may be pointing it to a location that does not exist.
Try ommiting the location and soap_version from the SoapClient constructor and let the library handle those parameter automatically:
$connection = new SoapClient($wsdl_url, array('connection_timeout'=> 600,
'proxy_login' => "my_login", 'proxy_password' => "my_password"));
On the other hand, perhaps you are dealing with an extremely slow Web Service. There are many parameters in PHP that may be affecting the time it takes to timeout and most likely they are well below your connection_timeout parameter:
default_socket_timeout
max_execution_time
You have missed proxy_host and proxy_port in the options... If you need proxy to work provide theese parameters:
....
$connection = new SoapClient($wsdl_url, array(
'location' => $location,
'soap_version' => SOAP_1_1,
'connection_timeout'=> 600,
'proxy_host' => '....', // Your proxy host
'proxy_port' => 8080, // Your proxy port
'proxy_login' => "my_login",
'proxy_password' => "my_password"
));
Your first action when coding in the language you don't really know should be using manuals and examples.
Here is a link to PHP Manual on SoapClient - http://php.net/manual/en/soapclient.soapclient.php
For debugging SoapClient you can pass "trace" argument. Quote from the manual: Setting the boolean trace option enables use of the methods SoapClient->__getLastRequest, SoapClient->__getLastRequestHeaders, SoapClient->__getLastResponse and SoapClient->__getLastResponseHeaders.
$client = new SoapClient("some.wsdl", array('trace' => true));
So, in your case if you want to see what is going wrong do this:
$client = SoapClient($wsdl_url, array('trace' => 1));
$result = $client->SomeFunction();
echo "REQUEST HEADERS:\n" . $client->__getLastRequestHeaders() . "\n";
echo "REQUEST:\n" . $client->__getLastRequest() . "\n";
echo "Response headers:\n" . $client->__getLastResponseHeaders() . "\n";
echo "Response:\n" . $client->__getLastResponse() . "\n";
Also, as was noted in another answer - you are setting proxy_login and proxy_password parameters in the request, which should only be used if you are using proxy server to connect to that WSDL service.
I am using FreshDesk API as a ticketing system. When trying to send an attachment, it was stated that it should be sent as multipart/form-data content-type. Could someone explain how this is done?!
How I am sending attachments:
$json = json_encode(
array(
"helpdesk_note" => array(
"body" => Input::get('reply'),
"user_id" => $requester_id,
"attachments" => Input::get('photo'),
"private" => true
)
)
);
I don't know how you're querying the API but in case you're using CURL, just set the appropriate header:
curl_setopt($ch , CURL_HTTPHEADER , "Content-Type: multipart/form-data" );
Personally I would recommend Guzzle which has a clean and straightforward API.
In Guzzle you can modify your headers in a more OO-Way. There are several ways to accomplish your task. On possible approach could be:
$client = new GuzzleHttp\Client();
$request = $client->createRequest('POST', 'https://url.com/to/post/to');
$request->setHeader('content-type', 'multipart/form-data');
// Set the data you need to
$response = $client->send($request);
var_dump($response);
Guzzle btw, is a piece of cake to integrate with Laravel. Just require it in your composer.json and you're good to go!
I need to make call to SOAP 1.1 web service in PHP. However, one of requirements in order to work is that I must send Content-Type=application/soap+xml. Now, I know that these are differences:
SOAP 1.2 -> Content-Type: application/soap+xml
SOAP 1.1 -> Content-Type: text/xml
I need to use SoapClient with WSDL to do this. However, I couldn't find how to set Content-Type after I set version to SoapClient to 1.1
Could someone provide example or code snippet?
Thank you!
There are a number of headers that are ignored when you try to set them in a stream_context, and will never be used.
Check the underlying C source file soap/php_http.c and search for "skip some predefined headers" for a list.
This is the reason some people report problems trying to set Host, Authorization, Content-Type and other headers in certain situations.
You can supply a stream context in the SoapClient options.
$ctx_opts = array(
'http' => array(
'header' => 'Content-Type: application/soap+xml'
)
);
$ctx = stream_context_create($ctx_opts);
$soapClient = new SoapClient('your.wsdl', array('stream_context' => $ctx));
I am working with a third party service that requires me to authenticate through OAuth1 to make requests. I can successfully authenticate and get data back with most of their calls using GET, however some calls they require POST and this is where the problem lies. To authenticate I am using the below:
$oauth = new OAuth(MY_KEY,MY_SECRET);
$oauth->setNonce(rand());
$oauth->setToken('','');
Then for a GET call I am doing something like below:
$array = array(
'partnerId'=>'1234'
);
$call = $oauth->fetch("https://domain.co.uk/api/getInfo/",$array);
$data = $oauth->getLastResponse();
This all works perfectly, and I can print out the $data
However with POST calls:
$oauth = new OAuth(MY_KEY,MY_SECRET);
$oauth->setNonce(rand());
$oauth->setToken('','');
$oauth->enableDebug();
$oauth->setAuthType(OAUTH_AUTH_TYPE_AUTHORIZATION);
$array = array(
'rid' => "$restaurantId",
'datetime' => "$datetime",
'partysize' => $covers,
'timesecurityID' => "$securityId",
'resultskey' => "$resultskey"
);
$call = $oauth->fetch("https://domain.co.uk/api/booking/?pid=1234&st=0",$array,OAUTH_HTTP_METHOD_POST);
$data = $oauth->getLastResponse();
I keep getting the error: Invalid Consumer Signature
Speaking to their tech guy he suggested
Your sbs value indicates that you’re signing with all of the POST
parameters, whereas you only need to sign with the query string. In
this case it would be “pid=1234&st=0”. Unfortunately, I’m not
familiar with the PHP libs and don’t have any recommendations on how
to alter the behavior.
and also mentioned common previous problems with a PHP implementation are:
The PHP HTTP lib will drop the query string once the method is
changed from GET to POST.
The PHP oAuth lib will use the post data
to sign the request rather than the query string or maybe both.
If I dump out the headers I get:
[sbs] => POST&https%3A%2F%2Fdomain.co.uk%2Fapi%2Fbooking%2F&datetime%3D2013-02-21T10%253A30%253A00%26oauth_consumer_key%3DMySiteV3TT%26oauth_nonce%3D1213111151%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1360835965%26oauth_token%3D%26oauth_version%3D1.0%26partysize%3D2%26pid%3D1531%26resultskey%3DfoqgEnYK%25252bIzRd6BV3T8eGQ%25253d%25253d%26rid%3D31852%26st%3D0%26timesecurityID%3D349367809
[headers_sent] => POST /api/booking/?pid=1234&st=0 HTTP/1.1
It looks like it is sending the OAuth data with the rest of the post, I just want this sent in the Authorization header (which it is also sending)
Authorization: OAuth oauth_consumer_key="MySite",oauth_signature_method="HMAC-SHA1",oauth_nonce="1772854358",oauth_timestamp="1360768712",oauth_version="1.0",oauth_token="",oauth_signature="2%2B7xb%2BJ5cdbUDC5UHfsdfsNpFM1pE%3D"
So I think I need to strip the OAuth data from the post request but keep it as a Authorization Header but just can't find the magic way to do that!
I've seen this. In fetch(), try urlencoding your $array and passing it in as a string of the form:
rid=[restaurantId]&datetime=[datetime]&...
Also give the header (final parameter of fetch()):
Content-Type: application/x-www-form-urlencoded