3

Can anyone help figure out how to use AWS Signature, AWS Credentials and PHP SDK 3 to access an API Gateway API? It seems like AWS Signature does not actually attach headers to a Guzzle request.

Here is my code:

<?php

require 'vendor/autoload.php';

use Aws\Credentials\Credentials;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request;
use Aws\Signature\SignatureV4;

$access_key = '<access_key>';
$secret_key = '<secret_key>';
$url = 'https://<api-id>.execute-api.us-east-1.amazonaws.com/v1/camel?q=*';
$region = 'us-east-1';

$credentials = new Credentials($access_key, $secret_key);
var_dump($credentials);

$client = new Client();
$request = new Request('GET', $url);
var_dump($request);

$s4 = new SignatureV4("execute-api", $region);
$s4 = new SignatureV4("execute-api", "us-east-1");
$s4->signRequest($request, $credentials);
var_dump($s4);
var_dump($request);

$response = $client->send($request);

And the error I'm getting is:

( ! ) Fatal error: Uncaught exception 
'GuzzleHttp\Exception\ClientException' with message ' in 
/path/to/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php on 
line 113

( ! ) GuzzleHttp\Exception\ClientException: Client error: `GET 
https://<api-id>.execute-api.us-east-1.amazonaws.com/v1/camel?q=*` 
resulted in a `403 Forbidden` response: {"message":"Missing 
Authentication Token"} in 
/path/to/vendor/guzzlehttp/guzzle/src/Exception/RequestException.php on 
line 113
Call Stack
#   Time    Memory  Function    Location
1   0.0002  234048  {main}( )   ../access.php:0
2   0.2801  486272  GuzzleHttp\Client->send( )  ../access.php:29
3   0.3787  574224  GuzzleHttp\Promise\Promise->wait( ) ../Client.php:106

Line 29 of access.php is:

$response = $client->send($request);

It doesn't appear from the var_dumps that any headers are being added. I am able to successfully test this endpoint in the API Gateway and in Postman. Enabling CORS does not appear to make a difference.

Has anyone solved this issue yet?

This issue is also covered at https://forums.aws.amazon.com/post!reply.jspa?messageID=795522 and https://forums.aws.amazon.com/thread.jspa?messageID=774631&tstart=0 but there are no solutions there.

5
  • Is $s4->signRequest() really supposed to be called in void context, or does it have a return value? Commented Aug 5, 2017 at 1:27
  • You are right, it does have a return value. I didn't read the documentation closely enough (docs.aws.amazon.com/aws-sdk-php/v3/api/…). Commented Aug 7, 2017 at 16:22
  • Nice work, solving that with little more than a suggestion of where you might want to start looking. I didn't actually know the answer off hand, but any time I see what looks like a discarded return value, that's a red flag that something potentially useful is being thrown away, even if it's just a true/false pass/fail indication that requires interrogating something else to retrieve an error message... or, in this case, a new request with all the signing info added. Commented Aug 7, 2017 at 18:08
  • You are right; I should have been more careful. In my defense, it seems that the return value for signRequest() changed between AWS PHP SDK versions 2 and 3. Commented Aug 8, 2017 at 18:49
  • My comment was intended as a compliment, along with an explanation of the general thought process that led me to make the suggestion of what you might be overlooking. If it came across as a criticism, I do apologize. Commented Aug 8, 2017 at 23:40

2 Answers 2

6

Thanks, Michael, for your help above.

You have to use the return from new SignatureV4, which is a modified request.

$s4 = new SignatureV4("execute-api", $region);
$signedrequest = $s4->signRequest($request, $credentials);

$response = $client->send($signedrequest);

echo $response->getBody();
Sign up to request clarification or add additional context in comments.

Comments

-2

Below code should work if you just want to query API GW services from php

<?php

 $curl = curl_init();

 curl_setopt_array($curl, array(
  CURLOPT_URL => "https://<endpoint>.execute-api.us-east-1.amazonaws.com/<service>",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => "{}",
  CURLOPT_HTTPHEADER => array(
    "Authorization: AWS4-HMAC-SHA256 Credential=<credentials>/20180705/us-east-1/execute-api/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date, Signature=<signature>",
    "Cache-Control: no-cache",
    "Content-Type: application/json",
  ),
));

 $response = curl_exec($curl);
 $err = curl_error($curl);

 curl_close($curl);

 if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}

?>

1 Comment

Please use AWS Client API as it's standard

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.