I'm new to AWS can anyone please help me how to generate session token using STS API to upload files to S3
Brief:
I went through AWS documentation and researched on Google I have found below library for codeigniter to upload files to S3
https://github.com/psugand/CodeIgniter-S3
It is working fine and I'm able to upload files using my Access ID and secret key. But our requirement is to generate get temporary credentials from Amazon and send to iOS developers so that they can upload files directly to S3. I found below link on Amazon documentation where I need to follow 4 Tasks to get the temporary credentials.
http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
But some how response always says signature that I'm creating is not matching. Below is my code and response from Amazon. If I'm doing anything wrong please help me.
$AWSAccessKeyId = "AKIAIK4R57JLAFN4Z5SA";
$SecretAccessKey = AMAZON_SECRET_KEY;
$region = 'us-east-1';
$service = 'sts';
$term = 'aws4_request';
$Timestamp = gmdate('D, d M Y H:i:s') . ' GMT';
$date = gmdate('Ymd\THis\Z');
$credentialsScope = gmdate('Ymd').'/'.$region.'/'.$service.'/'.$term;
$credentials = $AWSAccessKeyId.'/'.$credentialsScope;
Task 1 Canonical Request
$CanonicalRequest =
'POST'."\n".
"/"."\n".
'Action=GetSessionToken&Version=2011-06-15&Name=Aasim&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=3600&X-Amz-Credential='.$credentials.'&X-Amz-Date='.$date.'&X-Amz-SignedHeaders=content-type%3Bhost%3Bx-amz-date'."\n".
'content-type:text/xml;charset=utf-8'."\n".
'host:sts.amazonaws.com'."\n".
'x-amz-date:'.$Timestamp."\n"."\n".
'content-type;host;x-amz-date'."\n".
hash("sha256",'UNSIGNED-PAYLOAD')."\n";
$hashedRequestPayload = hash("sha256",$CanonicalRequest);
Task 2 creating String-to-sign
$StringToSign =
'AWS4-HMAC-SHA256'."\n".
$date."\n".
$credentialsScope."\n".
$hashedRequestPayload;
Task 3 Calculating Signature
$kDate = hash_hmac('sha256', 'AWS4'.$SecretAccessKey, gmdate('Ymd'));
$kRegion = hash_hmac('sha256', $kDate,'us-east-1');
$kService = hash_hmac('sha256', $kRegion,'sts');
$kSigning = hash_hmac('sha256', $kService,'aws4_request');
$Signature = hash_hmac('sha256',$kSigning, $StringToSign);
Task 4 Add the Signing Information to the Request
$querystring = 'Action=GetSessionToken';
$querystring .= '&Version=2011-06-15';
$querystring .= '&X-Amz-Algorithm=AWS4-HMAC-SHA256';
$querystring .= '&X-Amz-Credential='.$credentials;
$querystring .= '&X-Amz-Date='.$date;
$querystring .= '&X-Amz-Expires=3600';
$querystring .= '&X-Amz-SignedHeaders=content-type%3Bhost%3Bx-amz-date';
$querystring .= '&X-Amz-Signature='.$Signature;
Executing Curl Request
$endpoint = 'https://sts.amazonaws.com/?'.$querystring;
$headers = array(
'x-amz-date:'.$Timestamp,
'host:sts.amazonaws.com',
'content-type: text/xml;charset=utf-8',
);
$session = curl_init($endpoint); // create a curl session
curl_setopt($session, CURLOPT_POST, true); // POST request type
curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // return values as a string - not to std out
curl_setopt($session, CURLOPT_HTTPHEADER, $headers);
$responseXML = curl_exec($session);
print_r($responseXML);
Response from Amazon
<ErrorResponse xmlns="https://sts.amazonaws.com/doc/2011-06-15/">
<Error>
<Type>Sender</Type>
<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.
The Canonical String for this request should have been
'POST
/
Action=GetSessionToken&Version=2011-06-15&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIK4R57JLAFN4Z5SA%2F20141231%2Fus-east-1%2Fsts%2Faws4_request&X-Amz-Date=20141231T065554Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=content-type%3Bhost%3Bx-amz-date
content-type:text/xml;charset=utf-8
host:sts.amazonaws.com
x-amz-date:Wed, 31 Dec 2014 06:55:54 GMT
content-type;host;x-amz-date
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'
The String-to-Sign should have been
'AWS4-HMAC-SHA256
20141231T065554Z
20141231/us-east-1/sts/aws4_request
c3ec81de483674a1bf52b60307ae36a4b5e00cff6c85a30f07cc5d00eeb0d699'
</Message>
</Error>
<RequestId>15cb2945-90ba-11e4-829d-1362e6783c1f</RequestId>
</ErrorResponse>
I'm using codeiginter for this. Please help me . Thanks in advance
Related
It is my first time trying to connect Sheet from Smartsheet using API with PHP.
I cannot seem to connect and give me this error
Notice: Trying to get property of non-object in C:\xampp\htdocs\smartsheet\test.php on line 22
The variable $sheetObj is empty.
And in Authorization: Bearer, what does Bearer means? Is it a token name or it is always Bearer?
My future plan is to write into the row of smartsheet using PHP. Can anyone give me advice what went wrong with my code?
$baseURL = "https://api.smartsheet.com/1.1";
$sheetsURL = $baseURL . "/sheets/";
$getSheetURL = $baseURL . "/sheet/xxxxxxxxxxx";
$rowsURL = $baseURL . "/sheet/xxxxxxxxxxx/rows";
$accessToken = "xxxxxxxxxxxxxxxxxx";
// Create Headers array for cURL
$headers = array(
"Authorization: Bearer " . $accessToken,
"Content-Type: application/json"
);
$curlSession = curl_init($getSheetURL);
curl_setopt($curlSession, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curlSession, CURLOPT_RETURNTRANSFER, TRUE);
$getSheetResponseData = curl_exec($curlSession);
$sheetObj = json_decode($getSheetResponseData);
echo "<h1>Sheet name: ". $sheetObj->name ."</h1>";
Both stmcallister and Kim provided good information on how to troubleshoot your issue and some likely causes.
There were actually two issues with the code you provided.
As Scott mentioned you must point to the 2.0 version of the API.
$baseURL = "https://api.smartsheet.com/2.0";
You have a typo in your $getSheetURL. As is documented here the url is /sheets/{sheetId}. So your code should have the following:
$getSheetURL = $baseURL. "/sheets/xxxxxxxxxxx";
Here is your code in a working state. Make sure to replace YOUR_TOKEN and also take a look at the output from var_dump (which I added to your code) to see what message it gives you.
<?php
$baseURL = "https://api.smartsheet.com/2.0";
$getSheetURL = $baseURL. "/sheets/4925037959505796";
$accessToken = "YOUR_TOKEN";
$headers = array("Authorization: Bearer ". $accessToken);
$curlSession = curl_init($getSheetURL);
curl_setopt($curlSession, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curlSession, CURLOPT_RETURNTRANSFER, TRUE);
$getSheetResponseData = curl_exec($curlSession);
// Remove this line when done debugging
var_dump($getSheetResponseData);
$sheetObj = json_decode($getSheetResponseData);
echo "<h1>Sheet name: ". $sheetObj->name ."</h1>";
?>
Search here on SO for the (partial) error message "Trying to get property of non-object" and you'll see lots of related posts. Essentially, this error means that your code is treating something as an object that's not actually an object. This would happen, for instance, when you try to access the name property of $sheetObj if the API request had previously failed for some reason and the contents of $sheetObj is therefore not actually an object.
I'm not very familiar with PHP, but I'd suspect (based on the error message, combined with the fact that you say "var_dump($getSheetResponseData) is Bool(false)) that the "Get Sheet" request may not be returning a successful response. To troubleshoot, I'd suggest that you try running the exact same "Get Sheet" request (i.e., with identical URI, including sheet Id) using a tool like Postman (https://www.getpostman.com/) or via the commandline with cURL, and see if you get a successful response. If you can get your request working via Postman or cURL, it should be straightforward to update your code to send the same request, resulting in a successful response. See this section of the Smartsheet API docs for info about API Troubleshooting techniques using Postman or cURL: http://smartsheet-platform.github.io/api-docs/#api-troubleshooting.
Version 1.1 of the Smartsheet API is no longer supported. You'll want to use version 2.
To do this just change $baseURL to this:
$baseURL = "https://api.smartsheet.com/2.0";
Also, each of the objects in the API will be represented by plural endpoints. So, to get a sheet you'll use:
$getSheetURL = $baseURL. "/sheets/xxxxxxxxxxx";
To get the rows you'll use:
$rowsURL = $baseURL. "/sheets/xxxxxxxxxxx/rows";
Bearer is the type of Authorization header that you're passing to the API, and the type that is required by the Smartsheet API.
Hello to use the smartsheet API connection to PHP, the API version 2.0 is used, because the older version is obsolete, the code for the connection is as follows:
$baseURL = "https://api.smartsheet.com/2.0/sheets";
// Insertar access token generado en SmartSheet
$accessToken = "YOUR_TOKEN";
// Creación del Headers Array para el Curl
$headers = array(
"Authorization: Bearer $accessToken",
"Content-Type: application/json");
//Conexión de la API de SmartSheet
$curlSession = curl_init($baseURL);
curl_setopt($curlSession, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curlSession, CURLOPT_RETURNTRANSFER, true);
//Establece la sesión del Curl
$smartsheetData = curl_exec($curlSession);
// Asignar respuesta a un objeto PHP
$createObj = json_decode($smartsheetData);
I have an application that can upload video in my youtube account, but after upgrading to YouTube API v3, the situation is complicated.
it used this implementation https://developers.google.com/youtube/2.0/developers_guide_protocol_browser_based_uploading
Youtube authorization previously looked like this.
$postData = "Email=".urlencode(Config::$youtubeEmail)."&Passwd=".urlencode(Config::$youtubePassword)."&service=youtube&source=".urlencode(Config::$youtubeAppName);
$curl = curl_init("https://www.google.com/youtube/accounts/ClientLogin");
curl_setopt($curl,CURLOPT_HEADER,"Content-Type:application/x-www-form-urlencoded");
curl_setopt($curl,CURLOPT_POST,1);
curl_setopt($curl,CURLOPT_POSTFIELDS,$postData);
curl_setopt($curl,CURLOPT_SSL_VERIFYPEER,0);
curl_setopt($curl,CURLOPT_RETURNTRANSFER,1);
curl_setopt($curl,CURLOPT_SSL_VERIFYHOST,1);
$response = curl_exec($curl);
curl_close($curl);
//print_r($response);exit();
list($this->auth,$this->youtubeUser) = explode("\n",$response);
list($this->authLabel,$this->authValue) = array_map("trim",explode("=",$this->auth));
list($this->youtubeUserlabel,$this->youtubeUserValue) = array_map("trim",explode("=",$this->youtubeUser));
I received auth token, that was used to make auth token to youtube account, and was possible to upload video via form
$youtubeVideoKeywords = ""; // This is the uploading video keywords.
$youtubeVideoCategory = $this->getVideoCategory(); // This is the uploading video category. There are only certain categories that are accepted. See below the method
$data = '<?xml version="1.0"?'.'>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xmlns:yt="http://gdata.youtube.com/schemas/2007">
<media:group>
<media:title type="plain">'.stripslashes($youtubeVideoTitle).'</media:title>
<media:description type="plain">'.stripslashes($youtubeVideDescription).'</media:description>
<media:category scheme="http://gdata.youtube.com/schemas/2007/categories.cat">'.$youtubeVideoCategory.'</media:category>
<media:keywords>'.$youtubeVideoKeywords.'</media:keywords>
</media:group>
<yt:accessControl action="list" permission="denied"/>
</entry>';
$headers = array(
'Authorization: Bearer ' . $this->authValue,
'GData-Version: 2',
'X-GData-Key: key=' . Config::$youtubeDevKey,
'Content-Type: application/atom+xml; charset=UTF-8'
);
$curl = curl_init("http://gdata.youtube.com/action/GetUploadToken");
curl_setopt($curl,CURLOPT_USERAGENT,$_SERVER["HTTP_USER_AGENT"]);
curl_setopt($curl,CURLOPT_RETURNTRANSFER,true);
curl_setopt($curl,CURLOPT_TIMEOUT,10);
curl_setopt($curl,CURLOPT_SSL_VERIFYPEER,false);
curl_setopt($curl,CURLOPT_POST,1);
curl_setopt($curl,CURLOPT_FOLLOWLOCATION,1);
curl_setopt($curl,CURLOPT_HTTPHEADER,$headers);
curl_setopt($curl,CURLOPT_POSTFIELDS,$data);
curl_setopt($curl,CURLOPT_REFERER,true);
curl_setopt($curl,CURLOPT_HEADER,0);
$response = simplexml_load_string(curl_exec($curl));
curl_close($curl);
return $response;
I didnt need any permission to upload video, user only used form where writed title, desc etc, pickedup video from computer and hit upload. Everything was fine, users could upload video to my youtube channel. useres ere satisfied, because there wasn´t additional login.
But now, when i make curl request to https://www.google.com/youtube/accounts/ClientLogin it says, that i need oauth2.0 auth service.
So i generated oauth2 secrets to my service, made changes to code, but when user want to use this application he need to be logged to google account and allow permissions to use web app.
New code snippet:
When user want to upload video, i check for auth code from oauth2service, and make curl for code, if doesnt exist
$url = 'https://accounts.google.com/o/oauth2/v2/auth?';
$url .= 'client_id=' . Config::$googleClientId;
$url .= '&redirect_uri=' . urlencode(Config::$googleRedirectUrl);
$url .= '&scope=' . urlencode("https://www.googleapis.com/auth/youtube");
$url .= '&response_type=code';
It returns me to redirect page, when i get a code, store it to application for later use. User write down info about video, and hit next button. I make new thread to upload service, use generated code to get a access token, which is used to form upload
$curl = curl_init('https://accounts.google.com/o/oauth2/token');
$post_fields = array(
'code' => $code,
'client_id' => Config::$googleClientId,
'client_secret' => Config::$googleSecretCode,
'redirect_uri' => Config::$googleRedirectUrl,
'grant_type' => 'authorization_code'
);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_HEADER, 0);
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($post_fields));
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
'Content-Type: application/x-www-form-urlencoded'
));
//send request
$response = curl_exec($curl);
curl_close($curl);
$json = json_decode($response,true);
$this->authValue = $json["access_token"];
// The method returns response xml
$response = false;
$youtubeVideoKeywords = ""; // This is the uploading video keywords.
$youtubeVideoCategory = $this->getVideoCategory(); // This is the uploading video category. There are only certain categories that are accepted. See below the method
$data = '<?xml version="1.0"?'.'>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xmlns:yt="http://gdata.youtube.com/schemas/2007">
<media:group>
<media:title type="plain">'.stripslashes($youtubeVideoTitle).'</media:title>
<media:description type="plain">'.stripslashes($youtubeVideDescription).'</media:description>
<media:category scheme="http://gdata.youtube.com/schemas/2007/categories.cat">'.$youtubeVideoCategory.'</media:category>
<media:keywords>'.$youtubeVideoKeywords.'</media:keywords>
</media:group>
<yt:accessControl action="list" permission="denied"/>
</entry>';
$headers = array(
'Authorization: Bearer ' . $this->authValue,
'GData-Version: 2',
'X-GData-Key: key=' . Config::$youtubeDevKey,
'Content-Type: application/atom+xml; charset=UTF-8'
);
$curl = curl_init("http://gdata.youtube.com/action/GetUploadToken");
curl_setopt($curl,CURLOPT_USERAGENT,$_SERVER["HTTP_USER_AGENT"]);
curl_setopt($curl,CURLOPT_RETURNTRANSFER,true);
curl_setopt($curl,CURLOPT_TIMEOUT,10);
curl_setopt($curl,CURLOPT_SSL_VERIFYPEER,false);
curl_setopt($curl,CURLOPT_POST,1);
curl_setopt($curl,CURLOPT_FOLLOWLOCATION,1);
curl_setopt($curl,CURLOPT_HTTPHEADER,$headers);
curl_setopt($curl,CURLOPT_POSTFIELDS,$data);
curl_setopt($curl,CURLOPT_REFERER,true);
curl_setopt($curl,CURLOPT_HEADER,0);
$response = simplexml_load_string(curl_exec($curl));
curl_close($curl);
return $response;
Everything is fine, now i have got form, where action is to youtube server with token. everything work.
But now needs a user's consent to use web application, why? i have got my youtube account where videos are stored, no need information about user google account. Its very annoying, because user must be logged to application, after login it tells you, that must be logged to google account...
is there any workaround/solution that can make upload video to my youtube channel same easy as before without oauth2 please?
You need to setup with a service account which means assigning domain-wide delegations: https://console.developers.google.com/apis/credentials
"To support server-to-server interactions, first create a service account for your project in the API Console. If you want to access user data for users in your G Suite domain, then delegate domain-wide access to the service account.
Then, your application prepares to make authorized API calls by using the service account's credentials to request an access token from the OAuth 2.0 auth server.
Finally, your application can use the access token to call Google APIs."
Help: https://developers.google.com/identity/protocols/OAuth2ServiceAccount
Without a service account it will always require a human user to accept a consent screen, whereas the service account bypasses the consent screen making it applicable for such operations where you wish for it to run in the background. It's not covered very well in their documentation (or I have yet to find a good resource... even though I feel like I've turned every stone on this planet).
I am having trouble getting tokens to work but have gotten service scripts working before... perhaps we can help one another? http://fb.com/jmt193 :)
I will reply to questions if you ask them.
we are trying to access the Amazon MWS Api but we just can't get it to work and we don't know why.
This is what we have tried so far:
require_once('.config.inc.php');
$base_url = "https://mws.amazonservices.de/Products/2011-10-01";
$method = "POST";
$host = "mws.amazonservices.de";
$uri = "/Products/2011-10-01";
$params = array(
'AWSAccessKeyId' => <our Key>,
'Action' => "GetLowestOfferListingsForASIN",
'SellerId' => <our ID>,
'SignatureMethod' => "HmacSHA256",
'SignatureVersion' => "2",
'Timestamp'=> gmdate("Y-m-d\TH:i:s.\\0\\0\\0\\Z", time()), //tried this with time()+7200 since our server is 2 hours back but we also accessed mws to get the time used there
'Version'=> "2011-10-01",
'MarketplaceId' => <our MpID>,
'ItemCondition' => 'new',
'ASINList.ASIN.1' => B00NN8LSXY );
// Sort the URL parameters
$url_parts = array();
foreach(array_keys($params) as $key)
$url_parts[] = $key . "=" . str_replace('%7E', '~', rawurlencode($params[$key]));
sort($url_parts);
// Construct the string to sign
$url_string = implode("&", $url_parts);
$string_to_sign = "POST\nmws.amazonservices.de\n/Products/2011-10-01\n" . $url_string;
// Sign the request
$signature = hash_hmac("sha256", $string_to_sign, AWS_SECRET_ACCESS_KEY, TRUE);
// Base64 encode the signature and make it URL safe
$signature = urlencode(base64_encode($signature));
$url = "https://mws.amazonservices.de/Products/2011-10-01" . '?' . $url_string . '&Signature=' . $signature;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,$url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
$response = curl_exec($ch);
//$parsed_xml = simplexml_load_string($response);
echo $response;
//return ($parsed_xml);
in the .config.inc.php file we added
all the Keys & IDs +
define('APPLICATION_NAME', '<our Firm>');
define('APPLICATION_VERSION', '1.0');
before we did all that we checked everything in MWS-Scratchpad but everything seems to be working there (on mws.amazon.de).
But we still get the SignatureDoesNotMatch Errorcode
<Message>The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.</Message>
or this Errorcode:
<Message>Request signature is for too far in the future. Timestamp date: 2015-05-23T04:54:38.000Z. Currently, 10 percent of requests that are more than 15 minutes in the future will be rejected.</Message>
Hope someone can help we went through every other post and developer-guide about this - nothing seems to help
I had the same problem with it (among others).
The PHP reference and examples supplied by amazon has the answer buried deep within. The whole thing is a prime example of lasagna-code and reads like an insult to coders everywhere.
The API expects an HTTP POST with all the request data and a signiture made with your secret key. The sorting of the array and the url encoding standard changes the string to sign.
Amazon expects it sorted like this:
uksort($params, 'strcmp');
Forget the whole $url_parts part, it's messy. Use http_build_query() instead like this:
$url_string = http_build_query($params,'','&',PHP_QUERY_RFC3986);
Amazon expects RFC3986, so a space is encoded as '+', not '%20'. Also the timestamp should look like this:
'Timestamp' => gmdate("Y-m-d\TH:i:s\\Z", time()),
Good luck.
This happens when your machine's / Server's time is not correct. It happens to my servers sometimes when i reboot them. Just set the syncronization with time servers.
I have some trouble to get a simple xml answer from Amazon, it reports me always:
Sender
InvalidParameterValue
Either Action or Operation query parameter must be present.
And if I ask their Support, they can't help me they dont see the missing Parameter...
Their suggestion is follow their Examples, but my Webhost only supports php 5.2, so the autoloader doesn't work.
<?php
#header("Content-Type:text/xml");
$sellerID = 'SELLEDERID';
$aws = 'AWSKEY';
$secret = 'SECRET';
$action = 'GetReportList';
$timestamp = gmdate("Y-m-d\TH:i:s\Z");
$signature = $action . $timestamp;
$sig = base64_encode(hash_hmac("sha256", $signature, $secret, true));
$service = 'https://mws.amazonservices.com/?';
$url = 'AWSAccessKeyId='.$aws;
$url .= '&Action='.$action;
$url .= '&Merchant='.$sellerid;
$url .= '&SignatureVersion=2';
$url .= '&Timestamp=2013-01-10T12:22:48Z';
$url .= '&Version=2009-01-01';
$url .= '&Signature='.$sig;
$url .= '&SignatureMethod=HmacSHA256';
$awsURL = $service.urlencode($url);
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_URL => $awsURL,
CURLOPT_USERAGENT => 'Request'
));
$resp = curl_exec($curl);
curl_close($curl);
echo "<pre>";
var_dump($resp);
var_dump($awsURL);
echo "</pre>";
?>
The "we see no error in your request" probably referred to the request you put into Scratchpad, and not to the request you made through php, because your signature calculation is way off.
See this StackOverflow question or the MWS Developers Guide (page 12, "If you create your own client library") on how to calculate the sig.
The actual error message seems weird. I expect it to change once you've got your signature right. Please also note that quite a few MWS API calls require a HTTP POST, so if you intend to reuse that code in other places you're probably better off changing your code accordingly.
I'm trying to create a bucket on GCS using API v1.0 (interoperable mode) in PHP but I'm getting a 'signature does not match' error response.
Here's what I'm doing:
$access_id = "GOOGxxxxxx";
$secret_key = "xyxyxyxyx/xyxyxyxyx";
$bucket = "random_bucket_name";
$url = 'https://'.$bucket.'commondatastorage.googleapis.com';
$timestamp = date("r");
$canonicalizedResources = "/ HTTP 1.1";
$stringToSign = utf8_encode("PUT "."\n"."\n"."\n".$canonicalizedResources);
$signature = base64_encode(hash_hmac("sha1",$stringToSign,$secret_key,true));
$authSignature = $access_id.":".$signature;
$headers = array('Host: '.$bucket.'.commondatastorage.googleapis.com',
'Date: '.$timestamp, 'x-goog-api-version: 1',
'x-goog-project-id: xxxyyyxy','Content-Length: 0',
'Authorization: GOOG1 '.$authSignature);
$c = curl_init($url);
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($c,CURLOPT_HTTPHEADER,$headers);
$xml = curl_exec($c);
And here's the response that I get:
<?xml version='1.0' encoding='UTF-8'?>
<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you
provided. Check your Google secret key and signing method.</Message>
<StringToSign>
GET
Sat, 03 Mar 2012 14:56:53 -0800
x-goog-api-version:1
x-goog-project-id:xxxyyyxy
/random_bucket_name/
</StringToSign>
</Error>
Any ideas where I'm going wrong?
Here's Google's documentation on this:
https://developers.google.com/storage/docs/reference-methods#putbucket
One thing I noticed is that even though I specify "PUT" in the "stringToSign" variable ... the response says that I used "GET" ... ?
Any help would be appreciated.
There are a few problems here:
Your canonicalized resource should be "/bucket/", not "/ HTTP 1.1".
You need to include your two custom headers (x-goog-version and x-goog-project-id) in the string to sign.
The string to sign must include the timestamp sent in the Date: header.
You need to set CURLOPT_PUT so that curl knows to send a PUT request, rather than the default GET request (that's why your error response alludes to a GET request).
Here's a corrected version of your code, which I tested and used to create a new bucket:
<?php
$access_id = "REDACTED";
$secret_key = "REDACTED";
$bucket = "your-bucket";
$url = 'https://'.$bucket.'commondatastorage.googleapis.com';
$timestamp = date("r");
$version_header = "x-goog-api-version:1";
$project_header = "x-goog-project-id:REDACTED";
$canonicalizedResources = "/".$bucket."/";
$stringToSign = utf8_encode("PUT\n\n\n".$timestamp."\n".$version_header."\n".$project_header."\n".$canonicalizedResources);
$signature = base64_encode(hash_hmac("sha1",$stringToSign,$secret_key,true));
$authSignature = $access_id.":".$signature;
$headers = array('Host: '.$bucket.'.commondatastorage.googleapis.com',
'Date: '.$timestamp, $version_header,
$project_header,'Content-Length: 0',
'Authorization: GOOG1 '.$authSignature);
$c = curl_init($url);
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($c,CURLOPT_HTTPHEADER,$headers);
curl_setopt($c, CURLOPT_PUT, TRUE);
$xml = curl_exec($c);
print($xml);
?>
P.S. All the details on HMAC authentication for Google Cloud Storage are provided here: https://developers.google.com/storage/docs/reference/v1/developer-guidev1#authentication