I want to use an endpoint that uses an url parameter with spaces in it.
I have a function that generates an signature.
function generateSignature($request, $timestamp, $nonce)
{
Global $consumerKey, $accessToken, $consumerSecret, $tokenSecret, $realm, $signatureMethod, $version, $limit;
$baseparams = "";
$baseurl = strtok($request['url'], "?");
$base = strtoupper($request['method']) . "&" . urlencode($baseurl) . "&";
if(strpos($request['url'], '?') !== false){
$url_components = parse_url($request['url']);
parse_str($url_components['query'], $params);
$paramnr = 0;
foreach($params as $param => $value){
$paramnr++;
if($paramnr > 1){
$baseparams .= "&".$param."=" . urlencode($value);
}
else{
$baseparams .= $param."=" . urlencode($value);
}
}
$trailingand = "&";
}
else{
$trailingand = "";
}
echo $baseparams .= $trailingand
. "oauth_consumer_key=" . urlencode($consumerKey)
. "&oauth_nonce=" . urlencode($nonce)
. "&oauth_signature_method=" . urlencode($signatureMethod)
. "&oauth_timestamp=" . urlencode($timestamp)
. "&oauth_token=" . urlencode($accessToken)
. "&oauth_version=" . urlencode($version);
$base = $base.urlencode($baseparams);
$key = urlencode($consumerSecret) . '&' . urlencode($tokenSecret);
$signature = hash_hmac('sha256', $base, $key, true);
$signature = urlencode(base64_encode($signature));
return $signature;
}
My execute function:
function execute($options = array()){
Global $consumerKey, $accessToken, $consumerSecret, $tokenSecret, $realm, $signatureMethod, $version;
$curl = curl_init();
echo $options['url'];
curl_setopt_array($curl, array(
CURLOPT_URL => $options['url'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1
));
$timestamp = round(microtime(true));
$nonce = getNonce(11);
$signature = generateSignature($options, $timestamp, $nonce);
$authHeader = 'Authorization: OAuth realm="'.$realm.'",oauth_consumer_key="'.$consumerKey.'",oauth_token="'.$accessToken.'",oauth_signature_method="'.$signatureMethod.'",oauth_timestamp="'.$timestamp.'",oauth_nonce="'.$nonce.'",oauth_version="'.$version.'",oauth_signature="'.$signature.'"';
curl_setopt( $curl, CURLOPT_CUSTOMREQUEST, strtoupper($options['method']) );
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
$authHeader
));
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($curl, CURLINFO_HEADER_OUT, true);
$response = curl_exec($curl);
$array = json_decode($response, true);
echo "<br/>".$responsecode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if(curl_errno($curl)){
return 'Curl error: ' . curl_error($curl);
}
else{
return $array;
}
curl_close($curl);
}
This works fine when I have an url like this:
https://website.com/api/customer?limit=10 - WORKS
But the API also supports this:
https://website.com/api/customer?q=partner+EQUALS+10 - DOESNT WORK
When I put this URL into my function, it doesn't work and I get an 400 error instead.
My guess is that the signature is incorrect due to the spaces inside the url parameter.
How can I make this work with an url parameter that has spaces in it?
I already tried to use urlencode but that didnt work as well.
If it helps you out: I'm using the NETSUITE REST API for this.
Can someone help me plz...
When you make a req use encoded url insted of spaces.
https://website.com/api/customer?q=partner+EQUALS+10
this type will works for you
Related
What is wrong?
This is all my code, I think that it should be fine, but I don't know why... with postman I haven't errors and I receive the tokens while with Curl I receive this error. I'm working in local with Mamp pro.
The error received is:
My Code To generete the Signature and get the Token:
$url = "https://account.api.here.com/oauth2/token";
$data = array(
"grant_type" => "client_credentials"
);
$oauthNonce = mt_rand();
$oauthTimestamp = time();
$oauthCustomereKey = "******";
$oauthSignatureMethod = "HMAC-SHA256";
$httpMethod = "POST";
$oauthVersion = "1.0";
$keySecret = "*****";
$baseString = $httpMethod."&". urlencode($url);
$paramString =
"grant_type=client_credentials&".
"oauth_consumer_key=". urlencode($oauthCustomereKey).
"&oauth_nonce=". urlencode($oauthNonce).
"&oauth_signature_method=". urlencode($oauthSignatureMethod).
"&oauth_timestamp=". urlencode($oauthTimestamp).
"&oauth_version=". urlencode($oauthVersion)
;
$baseString = $baseString . "&" . urlencode($paramString);
var_dump($baseString);
$signingKey= urlencode($keySecret) . "&";
$signature = urlencode(
base64_encode(
hash_hmac(
'sha256',
$baseString,
$signingKey,
true
)
)
);
$params = [
"oauth_consumer_key" => $oauthCustomereKey,
"oauth_nonce" => $oauthNonce,
"oauth_signature_method" => $oauthSignatureMethod,
"oauth_timestamp" => $oauthTimestamp,
"oauth_version" => $oauthVersion,
"oauth_signature" => $signature
];
// $lol = 'oauth_consumer_key="' . $oauthCustomereKey . '",oauth_signature_method="'.$oauthSignatureMethod . '",oauth_timestamp="'.$oauthTimestamp.'",oauth_nonce="'.$oauthNonce.'",oauth_version="'.$oauthVersion.'",oauth_signature="'.$signature.'"';
/* This will give you the proper encoded string to include in your Authorization header */
$params = http_build_query($params, null, ',', PHP_QUERY_RFC3986);
$authorization = "Authorization: OAuth " . $params;
var_dump($authorization);
if(!$curl = curl_init()){
exit;
}
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
"Content-Type : application/x-www-form-urlencoded",
$authorization));
$token = curl_exec($curl);
curl_close($curl);
return $token;
Result basestring (dump on line 87):
string(253) "POST&https%3A%2F%2Faccount.api.here.com%2Foauth2%2Ftoken&grant_type%3Dclient_credentials%26oauth_consumer_key%3D********%26oauth_nonce%3D1468397107%26oauth_signature_method%3DHMAC-SHA256%26oauth_timestamp%3D1605523226%26oauth_version%3D1.0"
And on the doc here we have this example... it look the same:
POST
&https%3A%2F%2Faccount.api.here.com%2Foauth2%2Ftoken
&grant_type=client_credentials%26oauth_consumer_key%3Daccess-key-id-1234%26oauth_nonce%3DLIIpk4%26
oauth_signature_method%3DHMAC-SHA256%26oauth_timestamp%3D1456945283%26oauth_version%3D1.0
FIXED
Finally... after 2 days...
There are 2 errors on the code above:
The parameter $data to CURLOPT_POSTFIELDS
Oauth 1.0 wants the double quotes.
So change:
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
With
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data));
This is strange for me, because with curl I use http_build_query with GET request and not POST, where we can use $data directly like array.
And delete:
$params = http_build_query($params, null, ',', PHP_QUERY_RFC3986);
And write the query with your beautifuls hand because $params doesn't have double quote.
I am trying to get an access token in HERE REST API using PHP, but keep getting an error saying "Signature mismatch. Authorization signature or client credential is wrong.".
I'm following instructions on this page: https://developer.here.com/olp/documentation/access_control/api-reference-swagger.html but they don't provide clear example of generating the signature and I've tried every possible combination/method that I can think of.
Here is my code:
function getHereApiAccessToken()
{
$API_URL="https://account.api.here.com/oauth2/token";
$nonce=uniqid();
$signing_key="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxx-xxxxxxxxxxxxxxx-xxxxx_x-xxxxxxxxxxxxxx"; //here.access.key.secret
$signature_elements=array();
$signature_elements['oauth_consumer_key']="xxxx_xxxxxx-xxxxxxxxxx";
$signature_elements['oauth_nonce']=$nonce;
$signature_elements['oauth_signature_method']="HMAC-SHA256";
$signature_elements['oauth_timestamp']=time();
$signature_elements['oauth_version']="1.0";
ksort($signature_elements);
$base_string="";
foreach($signature_elements as $key=>$val)
{
$base_string.=urlencode($key).'='.urlencode($val).'&';
}
$base_string=rtrim($base_string, "&");
$signature=hash_hmac('sha256', $base_string, $signing_key, true);
$signature_base64=base64_encode($signature);
$headers=array();
$headers[]="Content-Type: application/x-www-form-urlencoded";
$headers[]='Authorization: OAuth oauth_consumer_key="'.urlencode($signature_elements['oauth_consumer_key']).'", oauth_nonce="'.urlencode($nonce).'", oauth_signature="'.urlencode($signature_base64).'", oauth_signature_method="'.urlencode($signature_elements['oauth_signature_method']).'", oauth_timestamp="'.time().'", oauth_version="'.urlencode($signature_elements['oauth_version']).'"';
$postData=array();
$postData['grant_type']="client_credentials";
$postData['expires_in']=50;
$ch=curl_init();
curl_setopt($ch, CURLOPT_URL, $API_URL);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
$response=curl_exec($ch);
br($response);
$httpcode=curl_getinfo($ch, CURLINFO_HTTP_CODE);
if(curl_error($ch))
{
echo "cURL error: ". curl_error($ch);
return false;
}
elseif($httpcode!=200)
{
echo "API responded with HTTP code: ". $httpcode;
echo "<br><br />Response: ".$response;
return false;
}
else
{
curl_close($ch);
$json=json_decode($response, 1);
br($json);
if(empty($json))
{
echo "Failed to decode JSON";
return false;
}
if(empty($json['access_token']))
{
echo "Missing access_token in API response: ".var_export($json, true);
}
return $json['access_token'];
}
return false;
}
And a copy of the error response:
{"errorId":"ERROR-c3d5c184-f2c9-4edb-a85c-2dd4c6d7e0d1","httpStatus":401,"errorCode":401300,"message":"Signature mismatch. Authorization signature or client credential is wrong.","error":"invalid_client","error_description":"errorCode: '401300'. Signature mismatch. Authorization signature or client credential is wrong."}
I also had same issue, here is working code:
function generate_string($input, $strength = 16) {
$input_length = strlen($input);
$random_string = '';
for($i = 0; $i < $strength; $i++) {
$random_character = $input[mt_rand(0, $input_length - 1)];
$random_string .= $random_character;
}
return $random_string;
}
$httpUrl = "https://account.api.here.com/oauth2/token";
$permitted_chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$nonce= generate_string($permitted_chars, 6);
$signature_elements=array();
$signature_elements['grant_type']="client_credentials";
$signature_elements['oauth_consumer_key']="#here.access.key.id#";//from config file
$signature_elements['oauth_nonce']=$nonce;
$signature_elements['oauth_signature_method']="HMAC-SHA256";
$signature_elements['oauth_timestamp']=time();
$signature_elements['oauth_version']="1.0";
$signing_key="#here.access.key.secret#"; //from config file
$base = 'POST'
. '&' . rawurlencode($httpUrl)
. '&' . rawurlencode('grant_type=client_credentials&oauth_consumer_key=' . $signature_elements['oauth_consumer_key'])
. rawurlencode('&oauth_nonce=' . $signature_elements['oauth_nonce'])
. rawurlencode('&oauth_signature_method=' . $signature_elements['oauth_signature_method'])
. rawurlencode('&oauth_timestamp=' . $signature_elements['oauth_timestamp'])
. rawurlencode('&oauth_version=' . $signature_elements['oauth_version']);
$key = rawurlencode($signing_key) . '&';
$signature = rawurlencode(base64_encode(hash_hmac('sha256', $base, $key, true)));
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => $httpUrl,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "grant_type=client_credentials",
CURLOPT_HTTPHEADER => array(
"authorization: OAuth oauth_consumer_key=\"" . $signature_elements['oauth_consumer_key'] . "\",oauth_signature_method=\"" . $signature_elements['oauth_signature_method'] . "\",oauth_timestamp=\"" . $signature_elements['oauth_timestamp'] . "\",oauth_nonce=\"" . $signature_elements['oauth_nonce'] . "\",oauth_version=\"" . $signature_elements['oauth_version'] . "\",oauth_signature=\"{$signature}\"",
"content-type: application/x-www-form-urlencoded",
),
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
echo '<pre>';
print_r(json_decode($response, true));
echo '</pre>';
I had the same problem. The solution was to read the documentation carefully
https://developer.here.com/documentation/identity-access-management/dev_guide/topics/sdk.html#step-2-create-a-signature
Especially this sentence needs attention:
The signing key is the URL-encoded access key secret, followed by an ampersand ("&").
I have done google api integration code as well as file is upload to a drive. I have issue regarding a uploaded file name is "Untitled". Please review code and guide me what is missing.
<?php
$GAPIS = 'https://www.googleapis.com/';
$GAPIS_AUTH = $GAPIS . 'auth/';
$GOAUTH = 'https://accounts.google.com/o/oauth2/';
$CLIENT_ID = '709846732498-t19mhuuvq0nqtng5ogg8XXXXXX0im8.apps.googleusercontent.com';
$CLIENT_SECRET = 'XXXXXXXXKa';
$REDIRECT_URI = 'http' . ($_SERVER['SERVER_PORT'] == 80 ? '' : 's') . '://' . $_SERVER['SERVER_NAME'] . $_SERVER['SCRIPT_NAME'];
$SCOPES = array($GAPIS_AUTH . 'drive', $GAPIS_AUTH . 'drive.file', $GAPIS_AUTH . 'userinfo.email', $GAPIS_AUTH . 'userinfo.profile');
$STORE_PATH = 'credentials.json';
function uploadFile($credentials, $filename, $targetPath)
{
global $GAPIS;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $GAPIS . 'upload/drive/v2/files?uploadType=media');
//$content = { title "mypdf.pdf", description = "mypdf.pdf", mimeType = "application/pdf" };
$contentArry = array('title' =>'veridoc' );
$contentArry = json_encode($contentArry);
curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);
//curl_setopt($ch, CURLOPT_POSTFIELDS,$contentArry);
curl_setopt($ch, CURLOPT_POSTFIELDS, file_get_contents($filename));
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER,
array('Content-Type : application/pdf','Content-Length:' . filesize($filename),'Authorization: Bearer ' . getAccessToken($credentials))
);
$postResult = curl_exec($ch);
curl_close($ch);
return json_decode($postResult, true);
}
function getStoredCredentials($path)
{
$credentials = json_decode(file_get_contents($path), true);
if (isset($credentials['refresh_token']))
return $credentials;
$expire_date = new DateTime();
$expire_date->setTimestamp($credentials['created']);
$expire_date->add(new DateInterval('PT' . $credentials['expires_in'] . 'S'));
$current_time = new DateTime();
if ($current_time->getTimestamp() >= $expire_date->getTimestamp())
{
$credentials = null;
unlink($path);
}
return $credentials;
}
function storeCredentials($path, $credentials)
{
$credentials['created'] = (new DateTime())->getTimestamp();
file_put_contents($path, json_encode($credentials));
return $credentials;
}
function requestAuthCode()
{
global $GOAUTH, $CLIENT_ID, $REDIRECT_URI, $SCOPES;
$url = sprintf($GOAUTH . 'auth?scope=%s&redirect_uri=%s&response_type=code&client_id=%s&approval_prompt=force&access_type=offline',
urlencode(implode(' ', $SCOPES)), urlencode($REDIRECT_URI), urlencode($CLIENT_ID)
);
header('Location:' . $url);
}
function requestAccessToken($access_code)
{
global $GOAUTH, $CLIENT_ID, $CLIENT_SECRET, $REDIRECT_URI;
$url = $GOAUTH . 'token';
$post_fields = 'code=' . $access_code . '&client_id=' . urlencode($CLIENT_ID) . '&client_secret=' . urlencode($CLIENT_SECRET)
. '&redirect_uri=' . urlencode($REDIRECT_URI) . '&grant_type=authorization_code';
$ch = curl_init();
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_URL, $url);
$result = curl_exec($ch);
curl_close($ch);
return json_decode($result, true);
}
function getAccessToken($credentials)
{
$expire_date = new DateTime();
$expire_date->setTimestamp($credentials['created']);
$expire_date->add(new DateInterval('PT' . $credentials['expires_in'] . 'S'));
$current_time = new DateTime();
if ($current_time->getTimestamp() >= $expire_date->getTimestamp())
return $credentials['refresh_token'];
else
return $credentials['access_token'];
}
function authenticate()
{
global $STORE_PATH;
if (file_exists($STORE_PATH))
$credentials = getStoredCredentials($STORE_PATH);
else
$credentials = null;
if (!(isset($_GET['code']) || isset($credentials)))
requestAuthCode();
if (!isset($credentials))
$credentials = requestAccessToken($_GET['code']);
if (isset($credentials) && isset($credentials['access_token']) && !file_exists($STORE_PATH))
$credentials = storeCredentials($STORE_PATH, $credentials);
return $credentials;
}
$credentials = authenticate();
$result = uploadFile($credentials, 'veridoc.pdf', '');
if (!isset($result['id']))
throw new Exception(print_r($result));
else
echo 'File copied successfuly (file Id: ' . $result['id'] . ')';
echo '<pre>'; print_r($result);
`
going by the documentation at https://developers.google.com/drive/api/v3/reference/files/update , to rename a file, do a PATCH request to https://www.googleapis.com/drive/v3/files/<fileId> with the new name, in your case, i guess that would be
$postResult = curl_exec ( $ch );
$parsed = json_decode ( $postResult, true );
if (! $parsed || $parsed ['code'] !== 200) {
throw new \RuntimeException ( 'google api error: ' . $postResult );
}
$id = $parsed ['id']; // ?? wait, is it id or fileId?
curl_setopt_array ( $ch, array (
CURLOPT_URL => 'https://www.googleapis.com/drive/v3/files/' . urlencode ( $id ),
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => json_encode ( array (
'name' => basename ( $filename )
) ),
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_HTTPHEADER => array (
'Content-Type : application/json',
'Authorization: Bearer ' . getAccessToken ( $credentials )
)
) );
curl_exec($ch);
ps, don't set the Content-Length header manually when using CURLOPT_POSTFIELDS, because curl will do it for you, and if you're using multipart/form-data, the length you set yourself is very likely to be wrong even (doesn't concern your code as you're not using multipart/form-data, but it's a good habit to learn nonetheless, get rid of it.)
I am trying to delete object from amazon s3 with REST API.
I create a curl command but I get 403 error: "SignatureDoesNotMatchThe request signature we calculated does not match the signature you provided".
I used in AWS documentation:
https://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectDELETE.html
https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
This is my code:
$host = 's3.amazonaws.com';
$url = "https://" . $bucket . '.' . $host . '/' . $file;
$date_full = date('D, d M Y H:i:s \G\M\T');
$date = date('Ymd');
$longDate = gmdate('Ymd\THis\Z');
$shortDate = gmdate('Ymd');
$credential = $accessKeyId . '/' . $shortDate . '/' . $region . '/s3/aws4_request';
$hashed_payload = strtolower(hash("sha256", ""));
$headers = [
'X-Amz-Algorithm' => 'AWS4-HMAC-SHA256',
'X-Amz-Credential' => $credential,
'X-Amz-Date' => $longDate,
'X-Amz-Expires' => 86400,
'X-Amz-SignedHeaders' => 'host',
'x-amz-content-sha256' => $hashed_payload,
'Host' => $bucket . '.' . $host,
];
ksort($headers);
$signed_headers_string = strtolower(implode(';', array_keys($headers)));
// Build canonical request
$canonical_request = "DELETE\n"; // HTTPRequestMethod
$canonical_request .= '/' . urlencode("http://s3.amazonaws.com/" . $bucket . "/" . $file) . "\n"; // CanonicalURI
$canonical_request .= "\n"; // CanonicalQueryString
foreach ($headers as $header => $value) {
$canonical_request .= strtolower($header) . ':' . trim($value) . "\n"; //CanonicalHeaders
}
$canonical_request .= $signed_headers_string . "\n"; // SignedHeaders
$canonical_request .= $hashed_payload; // HashedPayload
// Build string to sign
$string_to_sign = "AWS4-HMAC-SHA256\n";
$string_to_sign .= $date_full . "\n";
$string_to_sign .= $date . '/' . $region. "/s3/aws4_request\n";
$string_to_sign .= hash('sha256', $canonical_request);
// Calculate signature
$signature_date = hash_hmac('sha256', $date, 'AWS4' . $secretKey, true);
$signature_region = hash_hmac('sha256', $region, $signature_date, true);
$signature_service = hash_hmac('sha256', 's3', $signature_region, true);
$signature_request = hash_hmac('sha256', 'aws4_request', $signature_service, true);
// Build Signature
$signature = hash_hmac('sha256', $string_to_sign, $signature_request);
// Calculate final Authorization header
$headers['Authorization'] = 'AWS4-HMAC-SHA256 Credential=' . $credential . ', ';
$headers['Authorization'] .= 'SignedHeaders=' . $signed_headers_string . ', ';
$headers['Authorization'] .= 'Signature=' . $signature;
// Convert headers to key:value strings
$curl_headers = array();
foreach ($headers as $header => $value) {
$curl_headers[] = "{$header}:{$value}";
}
// Init curl
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_VERBOSE, 1);
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_setopt($curl, CURLINFO_HEADER_OUT, true);
curl_setopt($curl, CURLOPT_HEADER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, $curl_headers);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE");
$result = curl_exec($curl);
Someone can help me please to understand what is the problem?
You are sorting keys that have upper and lower case letters. Use all lower case letters in the keys before calling ksort().
You are also including headers that don't need to be included. The documentation you referred to only asks for x-amz-date, x-amz-content-sha256, and host. Use only those headers.
$headers = [
'x-amz-date' => $longDate,
'x-amz-content-sha256' => $hashed_payload,
'host' => $bucket . '.' . $host,
];
ksort($headers);
Your canonical request should have an extra line break after adding the headers:
foreach ($headers as $header => $value) {
$canonical_request .= strtolower($header) . ':' . trim($value) . "\n"; //CanonicalHeaders
}
$canonical_request .= "\n";
I'm not sure if this change is necessary, but in $string_to_sign, I use the date format provided by $longDate, not $date_full:
// Build string to sign
$string_to_sign = "AWS4-HMAC-SHA256\n";
$string_to_sign .= $longDate . "\n";
$string_to_sign .= $date . '/' . $region. "/s3/aws4_request\n";
$string_to_sign .= hash('sha256', $canonical_request);
You are setting CURLOPT_CUSTOMREQUEST twice, first to PUT and then to DELETE. The last setting is likely the one being used, but I would remove curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PUT'); for the sake of clarity.
I have the following code in one of my classes:
public function build_httpheader ($options = NULL)
{
if (!$this->oauth_token && !$this->oauth_token_secret && !$this->http_method) {
throw new Exception('The oauth_token, oauth_token_secret and the http method must be set');
}
$url = $this->base_uri;
$url_curl = $this->base_uri;
if ($options) {
$this->options = $options;
$url .= '?';
$url_curl .= '?';
$count = count($options);
foreach($options as $key => $value) {
$count = $count--;
if ($count) {
$url .= '&'.$key.'='.$value;
$url_curl .= '&'.$key.'='.rawurlencode($value);
} else {
$url .= $key.'='.$value;
$url_curl .= $key.'='.rawurlencode($value);
}
}
}
$this->url = $url_curl;
/**
* #internal Create a 32 chars unique string to
* use as the nonce value
*/
list($usec, $sec) = explode(" ", microtime());
$usec = str_replace('0.', '', $usec);
$nonce_str = utf8_encode($sec.$usec.'ABCD'.$sec);
$oauth_nonce = md5($nonce_str);
/**
* #internal Create the initial oAuth array
*/
$oauth = array (
'oauth_consumer_key' => $this->consumer_key,
'oauth_nonce' => $this->$oauth_nonce,
'oauth_signature_method' => 'HMAC-SHA1',
'oauth_token' => $this->oauth_token,
'oauth_timestamp' => time(),
'oauth_version' => '1.0'
);
/**
* #internal generate basic info
*/
$t_oauth = array();
ksort($oauth);
foreach($oauth as $key=>$value){
$t_oauth[] = "$key=" . rawurlencode($value);
}
$base_info = $this->http_method."&" . rawurlencode($url) . '&' . rawurlencode(implode('&', $t_oauth));
$composite_key = rawurlencode($this->consumer_secret) . '&' . rawurlencode($this->oauth_token_secret);
$oauth['oauth_signature'] = base64_encode(hash_hmac('sha1', $base_info, $composite_key, true));
$oauth_string = 'Authorization: OAuth ';
$values = array();
foreach($oauth as $key=>$value) {
$values[] = "$key=\"" . rawurlencode($value) . "\"";
}
$oauth_string .= implode(', ', $values);
$this->http_headers = array ($oauth_string, 'Expect:');
}
public function tw_curl_api_call ()
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTPHEADER, $this->http_headers);
if ($this->http_method == 'POST') {
curl_setopt($ch, CURLOPT_POST, true);
}
if ( ($this->http_method != 'POST') && ($this->http_method != 'GET') ) {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $this->http_method);
}
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_URL, $this->url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
$result = json_decode(curl_exec($ch), TRUE);
$info = curl_getinfo($ch);
curl_close($ch);
return array ('result' => $result, 'info' => $info);
}
Another script uses this class as follows:
$options = array ('resources' => 'help,users,search,statuses');
$tw_wrapper->build_httpheader ($options);
$results = $tw_wrapper->tw_curl_api_call ();
I am however getting an error 215 (bad authentication data). Any ideas (I am aware that there are existing PHP oAuth classes and twitter wrappers, but it does not seem as if any of them has completely migrated to the 1.1 API).
Have you had a look at Codebird? It has support for 1.1 and works well for what I've needed. YMWV of course.
A simple example:
Codebird::setConsumerKey('KEY', 'SECRET');
$cb = Codebird::getInstance();
$cb->setToken('AUTH_KEY', 'AUTH_SECRET');
$result = $cb->statuses_userTimeline(array(
'include_rts' => true,
'screen_name' => 'hansnilsson',
));