Paypal IPN callback stopped working in sandbox environment - paypal

I'm having a problem with the paypal IPN callback. Paypal's IPN callback stopped working, in sandbox environment.
I've been testing my client's website, for the past weeks, and it has always been working correctly - The payment was made, and a callback IPN was sent back to the website, confirming the payment, and updating the website's database.
I haven´t change anything in my code, and it suddenly stopped working. The payment is still made and saved in the paypal account, but the IPN is always retrying... it doesn't complete.
Here's the code in use:
<?php
// STEP 1: read POST data
// Reading POSTed data directly from $_POST causes serialization issues with array data in the POST.
// Instead, read raw POST data from the input stream.
$raw_post_data = file_get_contents('php://input');
$raw_post_array = explode('&', $raw_post_data);
$myPost = array();
foreach ($raw_post_array as $keyval) {
$keyval = explode ('=', $keyval);
if (count($keyval) == 2)
$myPost[$keyval[0]] = urldecode($keyval[1]);
}
// read the IPN message sent from PayPal and prepend 'cmd=_notify-validate'
$req = 'cmd=_notify-validate';
if(function_exists('get_magic_quotes_gpc')) {
$get_magic_quotes_exists = true;
}
foreach ($myPost as $key => $value) {
if($get_magic_quotes_exists == true && get_magic_quotes_gpc() == 1) {
$value = urlencode(stripslashes($value));
} else {
$value = urlencode($value);
}
$req .= "&$key=$value";
}
// STEP 2: POST IPN data back to PayPal to validate
$ch = curl_init('https://www.sandbox.paypal.com/cgi-bin/webscr');
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));
// In wamp-like environments that do not come bundled with root authority certificates,
// please download 'cacert.pem' from [link removed] and set
// the directory path of the certificate as shown below:
// curl_setopt($ch, CURLOPT_CAINFO, dirname(__FILE__) . '/cacert.pem');
if( !($res = curl_exec($ch)) ) {
// error_log("Got " . curl_error($ch) . " when processing IPN data");
curl_close($ch);
exit;
}
curl_close($ch);
……
?>
Firing some outputs to text files, I discovered that it passes the first Step and stops on the Second, before
if( !($res = curl_exec($ch)) ) {
I've already submitted three help requests to PayPal, but still didn't get an answer from them.

The cURL request initiates a HTTP POST back to PayPal in order to validate the IPN message. If it stops at this step, it means you do receive the IPN POST from PayPal, but you've got a problem connecting back to PayPal.
What does // error_log("Got " . curl_error($ch) . " when processing IPN data"); contain?
In order to resolve this, test the HTTPS connection from your server to the following addresses:
https://www.paypal.com/cgi-bin/webscr?cmd=_notify-validate
https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_notify-validate
For example, you can run cURL directly from your server (assuming you've got SSH access);
curl -v https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_notify-validate
This should return something similar to the following:
$ curl -v https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_notify-validate
* About to connect() to www.sandbox.paypal.com port 443 (#0)
* Trying 173.0.82.77...
* connected
* Connected to www.sandbox.paypal.com (173.0.82.77) port 443 (#0)
* successfully set certificate verify locations:
* CAfile: /usr/ssl/certs/ca-bundle.crt
CApath: none
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Server finished (14):
* SSLv3, TLS handshake, Client key exchange (16):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSL connection using AES256-SHA
* Server certificate:
* subject: 1.3.6.1.4.1.311.60.2.1.3=US; 1.3.6.1.4.1.311.60.2.1.2=Delaware; businessCategory=Private Organization; serialNumber=3014267; C=US; postalCode=95131-2021; ST=California; L=San Jose; street=2211 N 1st St; O=PayPal, Inc.; OU=PayPal Production; CN=www.sandbox.paypal.com
* start date: 2011-09-01 00:00:00 GMT
* expire date: 2013-09-30 23:59:59 GMT
* common name: www.sandbox.paypal.com (matched)
* issuer: C=US; O=VeriSign, Inc.; OU=VeriSign Trust Network; OU=Terms of use at https://www.verisign.com/rpa (c)06; CN=VeriSign Class 3 Extended Validation SSL CA
* SSL certificate verify ok.
> GET /cgi-bin/webscr?cmd=_notify-validate HTTP/1.1
> User-Agent: curl/7.28.1
> Host: www.sandbox.paypal.com
> Accept: */*
>
* HTTP 1.1 or later with persistent connection, pipelining supported
< HTTP/1.1 200 OK
< Date: Wed, 14 Aug 2013 20:15:53 GMT
< Server: Apache
< X-Frame-Options: SAMEORIGIN
< Set-Cookie: xxxxx
< X-Cnection: close
< Set-Cookie: xxxx domain=.paypal.com; path=/; Secure; HttpOnly
< Set-Cookie: Apache=10.72.128.11.1376511353229960; path=/; expires=Fri, 07-Aug-43 20:15:53 GMT
< Vary: Accept-Encoding
< Strict-Transport-Security: max-age=14400
< Transfer-Encoding: chunked
< Content-Type: text/html; charset=UTF-8
<
* Connection #0 to host www.sandbox.paypal.com left intact
**INVALID* Closing connection #0**
* SSLv3, TLS alert, Client hello (1):
If you receive a timeout or an SSL handshake error, you will need to investigate this separately.

Related

MongoDB HTTPS Endpoints - access querystring in function

I have an HTTPS endpoint in MongoDB Realm. How can I get the values of individual query string parameters inside a function? I'm trying to use the Request object, but I can only get back the entire querystring.
Sample URL: https:///endpoint/search?param1=apple&param2=banana
The endpoint calls the function:
exports = function(){
query = context.request.rawQueryString;
return {query};
};
A GET request to the endpoint returns:
{
"query": "param1=apple&param2=banana"
}
How can I modify my function to get values of a named query string parameter, e.g.?
const queryParam1 = request.query.param1 would evaluate to "apple".
Thank you.
Grab the specific fields from the query object:
const {value1, value2} = query;
I believe the variable names need to match the query string parameters. For example, assume the following CURL command (notice the query string parameters):
curl --include \
--verbose \
--header "Accept: application/json" \
--header "Content-Type: application/json" \
--request POST "https://eastus2.azure.data.mongodb-api.com/app/simplewebservice-ryzao/endpoint/test?queryStringParam1=abc&queryStringParam2=123" \
--data '{ "fieldA": { "$int": "1" }, "fieldB": {"$date": "2022-09-26T22:20:17.123Z" } }'
Because I have two query string parameters I need to catch them using their specific names - queryStringParam1 and queryStringParam2. See the code listing below for a complete working function:
exports = function({ query, headers, body}, response) {
try {
if(body === undefined) {
throw new Error(`Request body was not defined.`)
}
const {queryStringParam1, queryStringParam2} = query;
console.log(queryStringParam1);
console.log(queryStringParam2);
console.log("contentTypes = " + headers["Content-Type"])
var bodyJson = EJSON.parse(body.text());
console.log(bodyJson);
var calculatedObject = new Object();
calculatedObject.queryStringParam1 = queryStringParam1;
calculatedObject.queryStringParam2 = queryStringParam2;
calculatedObject.requestPayloadFieldA = bodyJson.fieldA;
calculatedObject.requestPayloadFieldB = bodyJson.fieldB;
response.setStatusCode(200);
response.setBody(JSON.stringify(calculatedObject));
} catch (error) {
response.setStatusCode(400);
response.setBody(error.message);
console.log(error.message);
}
};
In this example, I simply repackage and reply with the same data as what was sent. The response payload combines query string variables and request payload variables into a single response payload.
Example of output:
barry#barry-laptop:~$ curl --include --verbose --header "Accept: application/json" --header "Content-Type: application/json" --request POST "https://eastus2.azure.data.mongodb-api.com/app/simplewebservice-ryzao/endpoint/test?queryStringParam1=abc&queryStringParam2=123" --data '{ "fieldA": { "$int": "1" }, "fieldB": {"$date": "2022-09-26T22:20:17.123Z" } }'
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying 20.71.94.254:443...
* Connected to eastus2.azure.data.mongodb-api.com (20.71.94.254) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* CAfile: /etc/ssl/certs/ca-certificates.crt
* CApath: /etc/ssl/certs
* TLSv1.0 (OUT), TLS header, Certificate Status (22):
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS header, Certificate Status (22):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS header, Finished (20):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.2 (OUT), TLS header, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=data.mongodb-api.com
* start date: Aug 8 12:45:54 2022 GMT
* expire date: Nov 6 12:45:53 2022 GMT
* subjectAltName: host "eastus2.azure.data.mongodb-api.com" matched cert's "eastus2.azure.data.mongodb-api.com"
* issuer: C=US; O=Let's Encrypt; CN=R3
* SSL certificate verify ok.
* Using HTTP2, server supports multiplexing
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* Using Stream ID: 1 (easy handle 0x56145e5b6e80)
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
> POST /app/simplewebservice-ryzao/endpoint/test?queryStringParam1=abc&queryStringParam2=123 HTTP/2
> Host: eastus2.azure.data.mongodb-api.com
> user-agent: curl/7.81.0
> accept: application/json
> content-type: application/json
> content-length: 78
>
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* We are completely uploaded and fine
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
* TLSv1.2 (IN), TLS header, Supplemental data (23):
* Connection state changed (MAX_CONCURRENT_STREAMS == 2147483647)!
* TLSv1.2 (OUT), TLS header, Supplemental data (23):
* TLSv1.2 (IN), TLS header, Supplemental data (23):
< HTTP/2 200
HTTP/2 200
< vary: Origin
vary: Origin
< x-appservices-request-id: 633227f5d92f1e1d9c61bb7b
x-appservices-request-id: 633227f5d92f1e1d9c61bb7b
< x-frame-options: DENY
x-frame-options: DENY
< date: Mon, 26 Sep 2022 22:30:13 GMT
date: Mon, 26 Sep 2022 22:30:13 GMT
< content-length: 148
content-length: 148
< content-type: text/plain; charset=utf-8
content-type: text/plain; charset=utf-8
< server: envoy
server: envoy
<
* Connection #0 to host eastus2.azure.data.mongodb-api.com left intact
{"queryStringParam1":"abc","queryStringParam2":"123","requestPayloadFieldA":{"$int":"1"},"requestPayloadFieldB":{"$date":"2022-09-26T22:20:17.123Z"}}

Call api with curl and TLS 1.2 Two Way with public certificates of entities

I am currently calling a service which requires mutual authentication with curl and ubuntu, currently
I have the following certificates certRoot.cer, certSub.cer, domain.com.cer and pubkey.pem, to add the certificates to the path /etc/ssl/certs/ca-certificates.crt transform them all to a format pem and i made the call:
curl -v \
--key /etc/ssl/certs/ca-certificates.crt \
-u "user:password" \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data-raw '{"info":"data"}' \
https://endpoint.com:4445/api/path
This call itself is correct and returns the following information:
* Trying ip...
* Connected to endpoint.com (ip) port 4445 (#0)
* found 136 certificates in /etc/ssl/certs/ca-certificates.crt
* found 536 certificates in /etc/ssl/certs
* ALPN, offering http/1.1
* SSL connection using TLS1.2 / ECDHE_RSA_AES_256_GCM_SHA384
* server certificate verification OK
* server certificate status verification SKIPPED
* common name: endpoint.com (matched)
* server certificate expiration date OK
* server certificate activation date OK
* certificate public key: RSA
* certificate version: #3
* subject: C=CO,ST=STATE,L=DATA,O=DATA,OU=Sistemas,CN=endpoint.com
* start date: Tue, 03 Sep 2019 14:42:57 GMT
* expire date: Thu, 02 Sep 2021 14:42:57 GMT
* issuer: C=CO,ST=DATA,L=DATA,L=ADDRESS,O=DATA,OU=Gerencia de Sistemas,CN=DATA Sub CA Terceros
* compression: NULL
* ALPN, server did not agree to a protocol
* Server auth using Basic with user 'user'
> POST api/path HTTP/1.1
> Host: endpoint.com:4445
> Authorization: Basic dXNycHJ1X2Jpb2NyZWRpdDpQc123YmExMjM7
> User-Agent: curl/7.47.0
> Accept: application/json
> Content-Type: application/json
> Content-Length: 920
>
* upload completely sent off: 920 out of 920 bytes
< HTTP/1.1 403 Forbidden
< Date: Tue, 06 Apr 2021 21:37:21 GMT
< Strict-Transport-Security: max-age=31536000; includeSubDomains
< Content-Security-Policy: frame-ancestors 'none'
< Content-Length: 234
< Content-Type: text/html; charset=iso-8859-1
<
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access /api/path
on this server.</p>
</body></html>
* Connection #0 to host endpoint.com left intact
but at the moment in which the call is verified from the api side, it indicates that the certificate was not sent and rejects the connection, I imagine that is why it returns the error 403 (forbidden).
I have also tried to do it by passing the certificates directly, but it returns this error:
curl -v \
--key pubkey.pem \
--cert domain.com.cer \
-u "user:password" \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data-raw '{"info":"data"}' \
https://endpoint.com:4445/api/path
In this case the answer is the following:
* Trying ip...
* Connected to domain.com (ip) port 4445 (#0)
* found 136 certificates in /etc/ssl/certs/ca-certificates.crt
* found 536 certificates in /etc/ssl/certs
* ALPN, offering http/1.1
* error reading X.509 key or certificate file: Error in parsing.
* Closing connection 0
curl: (35) error reading X.509 key or certificate file: Error in parsing.
I clarify that all this also happens through a vpn and the connection to this vpn is already ok, if anyone has any idea how I can solve this problem I thank you very much, it can be with using any language or terminal client.
To complete this call successfully I did it with python, then I leave the code that I use:
import socket
import ssl
host_addr = 'domain.com'
host_port = 4445
server_sni_hostname = 'domain.com'
server_cert = '../DESTINATION.cer' #CERTIFICATE OF DESTINATION, IN PEM FORMAT ( -----BEGIN CERTIFICATE----- ...... -----END CERTIFICATE----- )
client_cert = '../CUSTOMER_CERTIFICATE.cer' #CUSTOMER CERTIFICATE, IN PEM FORMAT ( -----BEGIN CERTIFICATE----- ...... -----END CERTIFICATE----- )
client_key = '../CUSTOMER_PRIVATE_KEY.key' #CUSTOMER PRIVATE KEY, IN PEM AND PKCS8 FORMAT (-----BEGIN PRIVATE KEY----- ....... -----END PRIVATE KEY-----)
basicAuthHeader = 'dXNlcjpwYXNzd29yZA==' #USER:PASSWORD >> BASE64
method = 'POST /path/of/service HTTP/1.1\r\n'
headers = 'Host:'+host_addr+'\r\nContent-Type:application/json\r\nAuthorization:'+basicAuthHeader+'\r\nAccept:application/json\r\n\r\n'
body = '{"data":"string","data1":"string","data2":"string","data3":"string","data4":"string"}'
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=server_cert)
context.load_cert_chain(certfile=client_cert, keyfile=client_key)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
conn = context.wrap_socket(s, server_side=False, server_hostname=server_sni_hostname)
conn.connect((host_addr, host_port))
print("SSL established. ")
print("Sending:")
conn.send(bytes(method+headers+body, 'utf-8'))
print("Receiving")
received = conn.recv(36000)
print(received)
print("Closing connection")
conn.close()
I hope it will be of help to you in the future, for me it was a long task until I reached the solution.

Tried to POST on HTTP/2, but libcurl is connecting on HTTP/1.1

I have developed a client application to connect custom server on HTTP/2 with libcurl. It is working perfectly on Ubuntu 18.04. By the way, in Ubuntu 16.04, it is choosing HTTP/1.1 rather then HTTP/2.
I've found that nghttp2 library is needed to be installed on 16.04, so I installed it, then I found that ALPN is offering h2 also. But eventually it still use HTTP/1.1.
CURL *curl;
CURLcode res = curl_global_init(CURL_GLOBAL_DEFAULT);
if(res != CURLE_OK) {
fprintf(stderr, "curl_global_init() failed: %s\n",
curl_easy_strerror(res));
return 1;
}
curl = curl_easy_init();
if(curl) {
struct curl_slist *headerList = agent->getHeaderList();
/* what call to write: */
curl_easy_setopt(curl, CURLOPT_URL, agent->getUrl().c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerList);
curl_easy_setopt(curl, CURLOPT_VERBOSE, ASR_CLIENT_DEBUG ? 1L : 0L);
CURLcode rv;
do { /* dummy loop, just to break out from */
rv = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
rv = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
rv = curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, ConnectAgent::headerCallback);
rv = curl_easy_setopt(curl, CURLOPT_HEADERDATA, agent);
rv = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, ConnectAgent::bodyCallback);
rv = curl_easy_setopt(curl, CURLOPT_WRITEDATA, agent);
rv = curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2_0);
rv = curl_easy_setopt(curl, CURLOPT_POST, 1L);
rv = curl_easy_setopt(curl, CURLOPT_READFUNCTION, ConnectAgent::readToVoiceServerCallback);
rv = curl_easy_setopt(curl, CURLOPT_READDATA, agent);
/* Perform the request, res will get the return code */
res = curl_easy_perform(curl);
/* Check for errors */
if(res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
/* we are done... */
} while(0);
/* always cleanup */
curl_easy_cleanup(curl);
}
curl_global_cleanup();
In Ubuntu 16.04, It seems that HTTP2 is ready, but actual connection is on HTTP/1.1
* Trying xx.xxx.xxx.xxx...
* Connected to xxx.xxx.xxx.xxx (xx.xxx.xxx.xxx) port xxxxx (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:#STRENGTH
* successfully set certificate verify locations:
* CAfile: -----------------------
CApath: none
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use h2
* Server certificate:
* subject: C=Unknown; ST=Unknown; L=Unknown; O=Mort Bay Consulting Pty. Ltd.; OU=Jetty; CN=jetty.eclipse.org
* start date: May 20 11:38:03 2015 GMT
* expire date: Aug 18 11:38:03 2015 GMT
* issuer: C=Unknown; ST=Unknown; L=Unknown; O=Mort Bay Consulting Pty. Ltd.; OU=Jetty; CN=jetty.eclipse.org
* SSL certificate verify result: self signed certificate (18), continuing anyway.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* TCP_NODELAY set
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7f40f00008c0)
> POST /voice HTTP/1.1
Built on Ubuntu 18.04 with same source code is working fine with connecting on HTTP/2
I have found that it is occurred with curl-7.46 and curl-7.47. When I built library with curl-7.58 and I built my application with it, the problem was solved and it worked.

HTTP 401 Basic Authentication error accessing Magento 2 Rest API

I am attempting to use the Rest API in Magento 2. I have a piece of PHP that uses cURL to first get an admin token for my Magento user, then use the token to return a piece of Magento data (in this example a list of product types). The first part returns a token with no problems, but the second part comes back with an HTTP 401 Basic Authentication error.
My code is:
<?php
// Get handle for token retrieval
$userData = array("username" => "user", "password" => "password!");
$ch = curl_init("https://my.magento/rest/V1/integration/admin/token/");
// Set options
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($userData));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: application/json", "Content-Length: " . strlen(json_encode($userData))));
curl_setopt($ch, CURLOPT_VERBOSE, true);
$verbose = fopen('/tmp/curl.log', 'w+');
curl_setopt($ch, CURLOPT_STDERR, $verbose);
// Get token
$token = curl_exec($ch);
echo "Token returned: " . $token . "<BR><BR>";
// Display log
rewind($verbose);
$verboseLog = stream_get_contents($verbose);
echo "Verbose information 1:\n<pre>", htmlspecialchars($verboseLog), "</pre>\n";
echo "About to get product<BR>";
// Get handle for product types
$ch = curl_init("https://my.magento/rest/V1/products/types/");
// Set options
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array("Content-Type: application/json", "Authorization: Bearer " . json_decode($token)));
curl_setopt($ch, CURLOPT_VERBOSE, true);
$verbose = fopen('/tmp/curl.log', 'w+');
curl_setopt($ch, CURLOPT_STDERR, $verbose);
// Get types
$result = curl_exec($ch);
echo "Result: " . $result . "<BR>";
// Display log
rewind($verbose);
$verboseLog = stream_get_contents($verbose);
echo "<BR>Verbose information 2:\n<pre>", htmlspecialchars($verboseLog), "</pre>\n";
?>
And the browser output is:
Tokenreturned: "t8iskt68xlo5frf9hhtc1lk8wmqzbzx8"
Verbose information 1:
* About to connect() to my.magento port 443 (#2)
* Trying 104.25.128.20...
* Connected to mymagento (nn.nn.nn.nn) port 443 (#2)
* CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
* SSL connection using TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
* Server certificate:
* subject: CN=ssl379212.cloudflaressl.com,OU=PositiveSSL Multi-Domain,OU=Domain Control Validated
* start date: Oct 26 00:00:00 2018 GMT
* expire date: May 04 23:59:59 2019 GMT
* common name: ssl379212.cloudflaressl.com
* issuer: CN=COMODO ECC Domain Validation Secure Server CA 2,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB
> POST /rest/V1/integration/admin/token/ HTTP/1.1
Host: sand2.firetoys.co.uk
Accept: */*
Content-Type: application/json
Content-Length: 48
* upload completely sent off: 48 out of 48 bytes
< HTTP/1.1 200 OK
< Date: Wed, 31 Oct 2018 12:50:01 GMT
< Content-Type: application/json; charset=utf-8
< Content-Length: 34
< Connection: keep-alive
< Set-Cookie: __cfduid=d69af7d1f0a1205231a8867c1f45875621540990201; expires=Thu, 31-Oct-19 12:50:01 GMT; path=/; domain=.my.magento; HttpOnly
< X-Frame-Options: SAMEORIGIN
< X-UA-Compatible: IE=edge
< Pragma: no-cache
< Expires: -1
< Cache-Control: no-store, no-cache, must-revalidate, max-age=0
< Accept-Ranges: bytes
< Set-Cookie: PHPSESSID=9p378rsfito8gfocnrufucssh6; expires=Wed, 31-Oct-2018 13:50:01 GMT; Max-Age=3600; path=/; domain=sand2.firetoys.co.uk; secure; HttpOnly
< Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
< Server: cloudflare
< CF-RAY: 47263eb629ea0ce9-LHR
<
* Connection #2 to host my.magento left intact
About to get product
Result:
Verbose information 2:
* About to connect() to my.magento port 443 (#3)
* Trying nn.nn.nn.nn...
* Connected to my.magento (nn.nn.nn.nn) port 443 (#3)
* CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
* SSL connection using TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
* Server certificate:
* subject: CN=ssl379212.cloudflaressl.com,OU=PositiveSSL Multi-Domain,OU=Domain Control Validated
* start date: Oct 26 00:00:00 2018 GMT
* expire date: May 04 23:59:59 2019 GMT
* common name: ssl379212.cloudflaressl.com
* issuer: CN=COMODO ECC Domain Validation Secure Server CA 2,O=COMODO CA Limited,L=Salford,ST=Greater Manchester,C=GB
> GET /rest/V1/products/types/ HTTP/1.1
Host: sand2.firetoys.co.uk
Accept: */*
Content-Type: application/json
Authorization: Bearer t8iskt68xlo5frf9hhtc1lk8wmqzbzx8
< HTTP/1.1 401 Unauthorized
< Date: Wed, 31 Oct 2018 12:50:01 GMT
< Content-Length: 0
< Connection: keep-alive
< Set-Cookie: __cfduid=d38c9e4bc3019d9ac55c7f68f5c5ca1161540990201; expires=Thu, 31-Oct-19 12:50:01 GMT; path=/; domain=.my.magento; HttpOnly
< X-Varnish: 7995397
< WWW-Authenticate: Basic
< Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
< Server: cloudflare
< CF-RAY: 47263eb70f5b3512-LHR
<
* Connection #3 to host my.magento left intact
When I try just browsing directly to https://my.magento/rest/V1/products/types/ I get a Magento error back saying I am not authorised for the Products resource, which I would expect as I am sending no token or login credentials, but at least it is getting through to Magento.
Any ideas??
I should add that the server is set for Basic authentication, and if I replace the Bearer auth with the necessary Basic auth in the header for the GET, it returns the Magento message about not having access to the resource, which is fair enough. So I guess there are two questions:
How can I get past the basic authentication and still include the bearer authentication in my GET request, given that you can't put two authentications into the header?
Why does the initial POST to get the token work without any basic auth??
"How can I get past the basic authentication and still include the bearer authentication in my GET request, given that you can't put two authentications into the header?"
Disable auth for /index.php/rest location (in webserver)
"Why does the initial POST to get the token work without any basic auth??"
If the POST location is protected then you should get a 401 response.
Did you put the username and password on url on post request? http://user:pass#my.magento/rest/V1/
By the way, putting user:pass#my.magento into URL, will be translated into Authorization: User .
But you also set Authorization: Bearer t8iskt68xlo5frf9hhtc1lk8wmqzbzx8 which will overwrite the http authentification Authorization.

401 Authorization Required: Failed to validate oauth signature and token

I've tried using Net::Twitter::Role::OAuth to add Sign in with Twitter to my application.
I've used this successfully in the past, but not with SSL enabled, which apparently is now required by the Twitter API. I have a controller action very similar to the examples in the synopsis:
sub twitter_authorize : Local {
my($self, $c) = #_;
my $nt = Net::Twitter->new(traits => [qw/API::RESTv1_1 OAuth/], %param);
my $url = $nt->get_authorization_url(callback => $callbackurl);
$c->response->cookies->{oauth} = {
value => {
token => $nt->request_token,
token_secret => $nt->request_token_secret,
},
};
$c->response->redirect($url);
}
However, this fails at the $nt->get_authorization_url() call with a 401 Unauthorized error.
Looking at the oauth/request_token docs, I tried running the request through cURL, as follows:
curl --request 'POST' 'https://api.twitter.com/oauth/request_token' --header 'Authorization: OAuth oauth_consumer_key="xxxx", oauth_nonce="xxxx", oauth_signature="xxxx", oauth_signature_method="HMAC-SHA1", oauth_timestamp="xxxx", oauth_callback="oob", oauth_version="1.0"' --verbose
And the response is as follows:
* About to connect() to api.twitter.com port 443 (#0)
* Trying 199.16.156.104...
connected
* Connected to api.twitter.com (199.16.156.104) port 443 (#0)
* successfully set certificate verify locations:
* CAfile: none
CApath: /etc/ssl/certs
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Server finished (14):
* SSLv3, TLS handshake, Client key exchange (16):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSL connection using AES128-SHA
* Server certificate:
* subject: C=US; ST=California; L=San Francisco; O=Twitter, Inc.; OU=Twitter Security; CN=api.twitter.com
* start date: 2014-08-03 00:00:00 GMT
* expire date: 2016-12-31 23:59:59 GMT
* subjectAltName: api.twitter.com matched
* issuer: C=US; O=VeriSign, Inc.; OU=VeriSign Trust Network; OU=Terms of use at https://www.verisign.com/rpa (c)10; CN=VeriSign Class 3 Secure Server CA - G3
* SSL certificate verify ok.
> POST /oauth/request_token HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-pc-linux-gnu) libcurl/7.19.7 OpenSSL/0.9.8k zlib/1.2.3.3 libidn/1.15
> Host: api.twitter.com
> Accept: */*
> Authorization: OAuth oauth_consumer_key="xxxx", oauth_nonce="xxxx", oauth_signature="xxxx", oauth_signature_method="HMAC-SHA1", oauth_timestamp="xxxx", oauth_callback="oob", oauth_version="1.0"
>
< HTTP/1.1 401 Authorization Required
< cache-control: no-cache, no-store, must-revalidate, pre-check=0, post-check=0
< content-length: 44
< content-security-policy-report-only: default-src https:; connect-src https:; font-src https: data:; frame-src https: http://*.twimg.com http://itunes.apple.com about: javascript:; img-src https: data:; media-src https:; object-src https:; script-src 'unsafe-inline' 'unsafe-eval' about: https:; style-src 'unsafe-inline' https:; report-uri https://twitter.com/i/csp_report?a=NVXW433SMFUWY%3D%3D%3D&ro=true;
< content-type: text/html; charset=utf-8
< date: Tue, 21 Oct 2014 10:29:57 UTC
< expires: Tue, 31 Mar 1981 05:00:00 GMT
< last-modified: Tue, 21 Oct 2014 10:29:57 GMT
< pragma: no-cache
< server: tsa_b
< set-cookie: _twitter_sess=BAh7CDoPY3JlYXRlZF9hdGwrCD2PQTJJAToHaWQiJTE3M2Q4OWIyZWE1Nzc1%250AZmYxMjRkYmUyZDVjOTBlYjQxIgpmbGFzaElDOidBY3Rpb25Db250cm9sbGVy%250AOjpGbGFzaDo6Rmxhc2hIYXNoewAGOgpAdXNlZHsA--b807e4ebb8d45756e9686971b951a549d0d83b61; domain=.twitter.com; path=/; secure; HttpOnly
< set-cookie: guest_id=v1%3A141388739758201626; Domain=.twitter.com; Path=/; Expires=Thu, 20-Oct-2016 10:29:57 UTC
< status: 401 Unauthorized
< strict-transport-security: max-age=631138519
< vary: Accept-Encoding
< x-connection-hash: 54a185631d5f0b3a3a9dc46fe1f40a57
< x-content-type-options: nosniff
< x-frame-options: SAMEORIGIN
< x-mid: 0258025664ce095129d0cc294100d71a2e6e66ac
< x-runtime: 0.01294
< x-transaction: 6fad295009a89877
< x-ua-compatible: IE=edge,chrome=1
< x-xss-protection: 1; mode=block
<
* Connection #0 to host api.twitter.com left intact
* Closing connection #0
* SSLv3, TLS alert, Client hello (1):
Failed to validate oauth signature and token
Weirdly, if I remove the oauth_callback key from the Authorization header, it works fine and I get the tokens. However, the API docs suggest that this parameter is required. Is there something wrong with how I'm passing in the oauth_callback item?
I've tried setting it to oob (which is supposed to work for "out of band" access). And I've copied the encoded URL from the API docs. It doesn't work with either.
As it works without oauth_callback, it's not a time issue on my machine, as is a commonly reported problem. I haven't tried sending the Net::Twitter request without the callback (I haven't checked if that's possible) but I assume that would fix it there too. However, I do need the request to have a valid callback URL or the user won't be redirected back to the application for the rest of the sign in flow.
Adam,
I was having a similar issue and after a thorough investigation of what other Twitter API wrapper libraries were doing discovered that the oauth_callback needed to be encoded twice.
On doing a retrospective? search to get a little more explanation/clarity I found the following SO answer Twitter Oauth URL encoding inconsistencies? which extremely succinctly explained the issue I was having.
Is this perhaps the issue you have encountered?