Having Trouble in Generating Amazon AWS Signature With PHP - php

I was trying to make a HTTP POST Request to amazon's AGCOD Service API , I followed all the steps to make a signature and all the hashed keys matched the instructions here, but I still got a "The security token included in the request is invalid." ERROR.
<?php
define('PARTNER_ID','Test');
define('ACCESS_KEY','fake-aws-key');
define('SECRET_KEY','fake-secret-key');
define('TIME_STAMP_ISO_8601',date('Ymd\THisO'));
//define('TIME_STAMP_ISO_8601','20140205T171524Z');
//define('TIME_STAMP','20140205');
define('TIME_STAMP',date('Ymd'));
define('REGION_CODE','us-east-1');
define('SERVICE_NAME','AGCODService');
$secretKey = 'AWS4'.SECRET_KEY;
$hashedDateKey = hash_hmac('sha256', TIME_STAMP, $secretKey, true);//41b8dd5e0d1716ba90401d46b58b12d500accdd2ea9c2b22a2d275946c9d978e
$hashedRegionKey = hash_hmac('sha256', REGION_CODE, $hashedDateKey, true);//7b47360ce7afbe1b839e0b0e55834df99979a5414bc7f846b17c9374d230d45d
$hashedServiceKey = hash_hmac('sha256', SERVICE_NAME , $hashedRegionKey, true);//68136b0a64b2d01c8934370288b46500243645e468f521503e0d1fa73526d409
$signingKey = hash_hmac('sha256', 'aws4_request', $hashedServiceKey,true);//27cb9f5b991c2933f5faae716e99bd50c66a45811b1424128269312bdd570dff
$payload = "<CreateGiftCardRequest><creationRequestId>Test001</creationRequestId><partnerId>Test</partnerId><value><currencyCode>USD</currencyCode><amount>10</amount></value></CreateGiftCardRequest>";
$hashedPayload = hash('sha256',$payload);//50bf24a091a7463bb4a2661f93a7299c94774bc81f9fddf02af2925922b869dc
$CanonicalRequestStr = "POST\n/CreateGiftCard\n\naccept:charset=UTF-8\ncontent-type:charset=UTF-8\nhost:agcod-v2-gamma.amazon.com\nx-amz-date:".TIME_STAMP_ISO_8601."\nx-amz-target:com.amazonaws.agcod.AGCODService.CreateGiftCard\n\naccept;content-type;host;x-amz-date;x-amz-target\n".$hashedPayload;
$hashedCanonicalRequest = hash('sha256',$CanonicalRequestStr);//7d9f2765e4f23e85d3dce4ae264dac4f784c152f3746aff45ac7f3afd7fad649
$str2Sign = "AWS4-HMAC-SHA256\n".TIME_STAMP_ISO_8601."\n".TIME_STAMP."/".REGION_CODE."/".SERVICE_NAME."/aws4_request\n".$hashedCanonicalRequest;
$signature = hash_hmac('sha256', $str2Sign, $signingKey);//e32110cf663ed86460621dff12bb1139afe29d015584d208df09f149fa1b69d1
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,'https://agcod-v2-gamma.amazon.com/CreateGiftCard');
curl_setopt($ch, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
//curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch,CURLOPT_POST,1);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'accept:charset=UTF-8',
'content-type:charset=UTF-8',
'host:agcod-v2-gamma.amazon.com',
'x-amz-date:'.TIME_STAMP_ISO_8601,
'x-amz-target:com.amazonaws.agcod.AGCODService.CreateGiftCard',
'Authorization:AWS4-HMAC-SHA256 Credential='.ACCESS_KEY.'/'.TIME_STAMP.'/us-east-1/AGCODService/aws4_request, SignedHeaders=accept;content-type;host;x-amz-date;x-amz-target,Signature='.$signature,
));
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
$return = curl_exec($ch);
$return_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
var_dump($return_code);
?>

The AWS SDK for PHP has a SignatureV4 implementation. There is no PHP client included for AGCOD, since it is not an AWS service, but the DynamoDB service works very similarly to how AGCOD works. I was able to hack the DynamoDB client to send requests to AGCOD instead.
<?php
require 'vendor/autoload.php'; // If installed via Composer.
// Instantiate DynamoDB client, but overwrite a bunch of the settings
// to work with the AGCOD service
$client = \Aws\DynamoDb\DynamoDbClient::factory([
'base_url' => 'https://agcod-v2-gamma.amazon.com',
'key' => 'AWS_ACCESS_KEY_ID',
'secret' => 'AWS_SECRET_ACCESS_KEY',
'region' => 'us-east-1',
'signature' => 'v4',
'signature.service' => 'AGCODService',
'signature.region' => 'us-east-1',
]);
// Add wire logger for debugging
$client->addSubscriber(\Guzzle\Plugin\Log\LogPlugin::getDebugPlugin());
$body = <<<JSON
{"requestId": "Awssb0327141418PM", "partnerId": "Awssb", "utcStartDate":
"2014-03-27T00:10:10Z", "utcEndDate": "2014-03-27T23:59:59Z", "pageIndex": 0,
"pageSize": 200, "showNoOps": "true"}
JSON;
try {
// Use underlying Guzzle feature to send request,
// to skip some of the DynamoDB-specific logic.
$response = $client->put('GetGiftCardActivityPage', [
'x-amz-target' => 'com.amazonaws.agcod.AGCODService.GetGiftCardActivityPage',
'accept' => 'application/json'
], $body)->send();
} catch (\Aws\Common\Exception\ServiceResponseException $e) {
echo $e; // This exception class has a __toString() method.
}
Note sure if this will help much, but it could be used as a starting point if you decide to cherry pick code from the AWS SDK for PHP.

Related

Change Slack Bot Icon from Post PHP

We post a message to a slack channel every time a customer does a specific task. We want to change the bot Icon based on what is being posted in the channel.
Is this possible?
public static function send_to_slack($message,$title = null){
$body = array();
$body['text'] = '';
$body['icon_url'] = '';
if(!empty($title)) $body['text'] .= "*$title*\n";
$body['text'] .= $message;
$iconURL = "https://img.icons8.com/emoji/96/000000/penguin--v2.png";
$body['icon_url'] .= $iconURL;
$json = json_encode($body);
//Test Slack Channel
$slack = "SLACKURL"
$response = wp_remote_post( $slack, array(
'method' => 'POST',
'body' => $json,
)
);
if ( is_wp_error( $response ) ) {
return true;
} else {
return true;
}
}
From Slack:
You cannot override the default channel (chosen by the user who installed your app), username, or icon when you're using Incoming Webhooks to post messages. Instead, these values will always inherit from the associated Slack app configuration.
*** UPDATE
I just ran into this situation. My old app was using the old web hooks. I had to create a new integration and ran into this issue. The simple solution is to install the slack app called "Incoming Webhooks". It's made by slack. It will allow you to generate an older style webhook that will allow you to post to any channel.
Found the correct way. Make sure you enable chat:write.customize in Slack oAuth Scope.
public static function send_to_slack($message,$title = null){
$ch = curl_init("https://slack.com/api/chat.postMessage");
$data = http_build_query([
"token" => "BOT-TOKEN",
"channel" => "CHANNELID", //"#mychannel",
"text" => $message, //"Hello, Foo-Bar channel message.",
"username" => "MySlackBot",
"icon_url" => $iconURL
]);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$result = curl_exec($ch);
curl_close($ch);
return $result;
}

Sign in with Apple = invalid_client

I'm facing a very bad issue because I read so many guides and tutorials and nothing works.
The result is always the same: {"error":"invalid_client"}
I get the code, identityToken and everything I need - except the call to https://appleid.apple.com/auth/token - because of invalid_client.
Here is my url for getting the code.
https://appleid.apple.com/auth/authorize?response_type=code&client_id=org.example.service&redirect_uri=https%3A%2F%2Fexample.org
So then I have the default workflow.
And after accepting / loggin in I will be redirected to my page.
https://example.org/?code=a277243e2ec324fb09ba1c3333a8e6576.0.abcde.u4xiTDP2qHXoNEaxrcrIGx
(When I'm using the JavaScript API I'll get other informations like state, code and id_token. I already tried it with the "code" there, too.)
Back to the main function.
This is my request for Apple.
'client_id' => 'org.example.service',
'client_secret' => JWT-Data encoded (OPENSSL_ALGO_SHA256) see below
'grant_type' => 'authorization_code',
'code' => 'a277243e2ec324fb09ba1c3333a8e6576.0.abcde.u4xiTDP2qHXoNEaxrcrIGx'
JWT Header:
{
"alg": "ES256",
"kid": "1ABC2345DE"
}
JWT Payload:
{
"iss": "1A234BCD56",
"iat": 1571269964,
"exp": 1571273564,
"aud": "https://appleid.apple.com",
"sub": "org.example.service"
}
Response:
{
"error": "invalid_client"
}
The useless error message of the world.
I dont know why the client should be invalid.
I have a key in https://developer.apple.com/account/resources/authkeys/list with downloaded file name AuthKey_1ABC2345DE.p8. (means 1ABC2345DE is my key id)
Then I have a native iOS app with identifier "org.example" and a service with identifier "org.example.service".
Its not working with both ids and mixed different things.
Nothing. invalid_client.
Can anyone help me please? I'm sitting here for hours and getting only invalid_client
My testing page:
<html>
<head>
</head>
<body>
<script type="text/javascript" src="https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js"></script>
<div id="appleid-signin" data-color="black" data-border="true" data-type="sign in" data-width="330px" data-height="100px"></div>
<script type="text/javascript">
AppleID.auth.init({
clientId : 'org.example.service',
scope : 'email',
redirectURI: 'https://example.org',
state : 'EN'
});
</script>
</body>
</html>
And PHP:
<?php
// index.php
// function by https://stackoverflow.com/q/56459075/1362858
function encode($data) {
$encoded = strtr(base64_encode($data), '+/', '-_');
return rtrim($encoded, '=');
}
// function by https://stackoverflow.com/q/56459075/1362858
function generateJWT($kid, $iss, $sub, $key) {
$header = [
'alg' => 'ES256',
'kid' => $kid
];
$body = [
'iss' => $iss,
'iat' => time(),
'exp' => time() + 3600,
'aud' => 'https://appleid.apple.com',
'sub' => $sub
];
$privKey = openssl_pkey_get_private($key);
if (!$privKey) return false;
$payload = encode(json_encode($header)).'.'.encode(json_encode($body));
$signature = '';
$success = openssl_sign($payload, $signature, $privKey, OPENSSL_ALGO_SHA256);
if (!$success) return false;
return $payload.'.'.encode($signature);
}
$client_id = 'org.example.service';
$data = [
'client_id' => $client_id,
'client_secret' => generateJWT('1ABC2345DE', '1A234BCD56', $client_id, file_get_contents('AuthKey_1ABC2345DE.p8')),
'code' => 'a277243e2ec324fb09ba1c3333a8e6576.0.abcde.u4xiTDP2qHXoNEaxrcrIGx',
'grant_type' => 'authorization_code'
];
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://appleid.apple.com/auth/token');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$serverOutput = curl_exec($ch);
curl_close ($ch);
/**
* {"error":"invalid_client"}
*/
var_dump($serverOutput);
The problem was this special encryption.
In this blog they use PHP for everything except the client_secret generation.
https://developer.okta.com/blog/2019/06/04/what-the-heck-is-sign-in-with-apple
And in the text the author explains this sentence:
Some JWT libraries don’t support elliptic curve methods, so make sure yours does before you start trying this out.
Now it's working fine with exactly the code in the top - only replaced the client_secret generation.
well, I faced with this error "invalid_client" and just discovered, that all credentials were changed in https://developer.apple.com/account/resources/identifiers/serviceId . I think, because of changing of our legal entity (as account holder). So just check your web app ID and .p8 key, is it exist.
p.s. interesting, I been here, in this question, about a year ago, and it helped me as described above (JWT ES256 algo). And now, I hope, I'll help someone with another solution :-)

Consuming cURL in restful codeigniter

In native PHP, I have a consuming restful server like this:
$url = "http://localhost/pln/api/json?rayon=$rayon&id_pel=$id_pel&nama=$nama";
$client = curl_init($url);
curl_setopt($client,CURLOPT_RETURNTRANSFER,true);
$respone = curl_exec($client);
$result = json_decode($respone);
How can I access cURL like this when using CodeIgniter?
There's no active cURL library around for CodeIgniter 3.x. There were one for CI 2.x which is no longer maintained.
Consider using Guzzle which is very popular and considered as a de-facto HTTP interfacing library for PHP. Here's an usage example from the docs:
$client = new GuzzleHttp\Client();
$res = $client->request('GET', 'https://api.github.com/user', [
'auth' => ['user', 'pass']
]);
echo $res->getStatusCode();
// "200"
echo $res->getHeader('content-type');
// 'application/json; charset=utf8'
echo $res->getBody();
// {"type":"User"...'
I also recommend using Requests which is inspired by Python Requests module and is way more easier than Guzzle to get started with:
$headers = array('Accept' => 'application/json');
$options = array('auth' => array('user', 'pass'));
$request = Requests::get('https://api.github.com/gists', $headers, $options);
var_dump($request->status_code);
// int(200)
var_dump($request->headers['content-type']);
// string(31) "application/json; charset=utf-8"
var_dump($request->body);
// string(26891) "[...]"
As CodeIgniter 3.x has support for Composer packages out of the box, you can easily install one of these packages through composer and start using it right away.
I stongly recommend you to not to go down the "Download Script" way as suggested in Manthan Dave's answer. Composer provides PHP with a sophisticated dependency management ecosystem; Utilize that! "Download This Script" dog days are over for good.
I used the following function in codeigniter for curl url and works fine, try it out:
function request($auth, $url, $http_method = NULL, $data = NULL) {
//check to see if we have curl installed on the server
if (!extension_loaded('curl')) {
//no curl
throw new Exception('The cURL extension is required', 0);
}
//init the curl request
//via endpoint to curl
$req = curl_init($url);
//set request headers
curl_setopt($req, CURLOPT_HTTPHEADER, array(
'Authorization: Basic ' . $auth,
'Accept: application/xml',
'Content-Type: application/x-www-form-urlencoded',
));
//set other curl options
curl_setopt($req, CURLOPT_RETURNTRANSFER, true);
curl_setopt($req, CURLOPT_SSL_VERIFYPEER, true);
curl_setopt($req, CURLOPT_TIMEOUT, 30);
//set http method
//default to GET if data is null
//default to POST if data is not null
if (is_null($http_method)) {
if (is_null($data)) {
$http_method = 'GET';
} else {
$http_method = 'POST';
}
}
//set http method in curl
curl_setopt($req, CURLOPT_CUSTOMREQUEST, $http_method);
//make sure incoming payload is good to go, set it
if (!is_null($data)) {
if (is_array($data)) {
$raw = http_build_query($data);
} else {
//Incase of raw xml
$raw = $data;
}
curl_setopt($req, CURLOPT_POSTFIELDS, $raw);
}
//execute curl request
$raw = curl_exec($req);
if (false === $raw) { //make sure we got something back
throw new Exception(curl_error($req) . $url, -curl_errno($req));
}
//decode the result
$res = json_decode($raw);
if (is_null($res)) { //make sure the result is good to go
throw new Exception('Unexpected response format' . $url, 0);
}
return $res;
}
You could use default Curl library of codeigniter:
$this->load->library('curl');
$result = $this->curl->simple_get('http://example.com/');
var_dump($result);
For more details refer this link :
https://www.formget.com/curl-library-codeigniter/
Adding to #sepehr answer. Requests library can be configured in a very easy way in codeigniter as described here
https://stackoverflow.com/a/46062566/2472685

Godaddy api authorization error

I am trying to develop client application for GoDaddy based on their API that they provide here https://developer.godaddy.com
And I have a problem with simple example, I am trying to use the next PHP code to check if domain available:
use GuzzleHttp\Client;
try {
$client = new Client([
'base_uri' => 'https://api.godaddy.com',
]);
$responce = $client->get(
'/v1/domains/available?domain=example.guru',
[
'headers' => [
'Authorization' => "sso-key $myKey:$mySecret",
'X-Shopper-Id' => "$myID",
'Accept' => 'application/json',
]
]
);
echo $responce->getBody();
} catch (Exception $e) {
echo $e->getMessage();
}
And all the time I get error: "Client error: 401". Same problem I have with using cURL library. I didn't find any online support.
I need help with it, can someone explain how I should authorize at their api service? Maybe I need to send any other http headers or additional params?
Are the key and secret you're using for production? When I go through the process, by default it creates a TEST key/secret, which I think are meant to go against https://api.ote-godaddy.com
If you are using production keys, try doing a manual Curl request from the command like; something like:
curl -H 'Authorization: sso-key {KEY}:{SECRET}' -H 'Content-Type: application/json' https://api.godaddy.com/v1/domains/available?domain=example.guru'
Let us know how it works out!
The problem was that I was using TEST {KEY}:{SECRET} and set wrong URL.
For the test {KEY}:{SECRET} URL has to be: https://api.ote-godaddy.com.
Also the method for checking domain availability (/v1/domains/available) doesn't need parameter 'X-Shopper-Id' in header. It works well without it. With parameter X-Shopper-Id request returns error "NOT_FOUND: The specified shopperId could not be found"(but it's other problem, maybe I didn't activate some option)
So if to take into account all changes, the working code for checking domain availability with test key/secret should be like this:
use GuzzleHttp\Client;
try {
$client = new Client([
'base_uri' => 'https://api.ote-godaddy.com'
]);
$responce = $client->get(
'/v1/domains/available?domain=example.guru',
[
'headers' => [
'Authorization' => "sso-key $myKey:$mySecret",
'Accept' => 'application/json',
]
]
);
echo $responce->getBody();
} catch (Exception $e) {
echo $e->getMessage();
}
I am using php and curl.
$domain = "jaisinghverma.com";<br>
$apiURL = 'https://api.ote-godaddy.com/v1/domains/available?
domain='.$domain.'&checkType=FULL&forTransfer=false';<br>
$headers = array(
'Accept: application/json',
'Authorization: sso-key ?????????',
);<br>
$ch = curl_init();<br>
curl_setopt($ch, CURLOPT_URL, $apiURL);<br>
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);<br>
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);<br>
$server_output = curl_exec ($ch);<br>
curl_close ($ch);<br>
print_r(json_decode($server_output));
above code is working fine for me.

Implement an Air API by sending a SOAP request

I have a php web site. Here I need to implement air ticket searching and booking functionality. In order to do this, I have used a paid API from ARZOO web site... I got all the documentation from ARZOO. I have read the entire doc. The Doc Says
"Accessing this service requires standard SOAP client. SOAP client should authenticate
itself through user name and password. Client should get their IPs registered with
Arzoo and get a user account created. The Arzoo web service provides a service
point URL. Web service clients should post SOAP request message as an attachment
for the desired response. The XML structure of different web services is discussed in
the respective documents."
You can connect to Arzoo XML services with the following test service point URLs:-
FlightAvailability:http://<url>/DOMFlightAvailability?wsdl
I think need to send the request via soap is it?
But in the air availability contains
Example Request Xml
<Request>
<Origin>BOM</Origin>
.............
.............
</Request>
I have used the following code
$post_string.="<Request>";
$post_string.="<Origin>$from</Origin><Destination>$to</Destination>";
........
......
$post_string.="</Request>";
$path = ":http://<url>/DOMFlightAvailability?wsdl";
$ch = curl_init($path);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_string); //Send the data to the file
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$val = curl_exec($ch);
$headers = curl_getinfo($ch);
$errr=curl_error($ch);
But it's not giving any result.
The documents says
RESPONSE XML:-
The response will be in <arzoo_response> </arzoo_response>. This contains the Request
also.
I don't know SOAP.
I am totally disappointed. Please Help me.
I think I will get an xml response after posting the request. But how I will post my data?
Please reply
Many thanks, if anyone help me, its great appreciable
i also getting error while using SOAP CLIENT but when i use nusoap then they give me result..using this code if u get error like ip/password mismatch then You will call arzoo to verify your clientid and clientpassword
<?php
ini_set('max_execution_time','180');
include 'lib/nusoap.php';
$location_URL ='http://avail.flight.arzoo.com';
$action_URL ='http://demo.arzoo.com/ArzooWS/services/DOMFlightAvailability?wsdl';
$Request = '<Request>
<Origin>BOM</Origin>
<Destination>DEL</Destination>
<DepartDate>2017-02-02</DepartDate>
<ReturnDate>2017-02-02</ReturnDate>
<AdultPax>1</AdultPax>
<ChildPax>0</ChildPax>
<InfantPax>0</InfantPax>
<Currency>INR</Currency>
<Clientid>Given by Arzoo.com</Clientid>
<Clientpassword>Given by Arzoo.com</Clientpassword>
<Clienttype>ArzooFWS1.1</Clienttype>
<Preferredclass>E</Preferredclass>
<mode>ONE</mode>
<PreferredAirline>AI</PreferredAirline>
</Request>';
$clientinfo = array('soap_version'=>SOAP_1_1,
'location' =>$location_URL,
'uri' =>$action_URL,
'style' => SOAP_RPC,
'use' => SOAP_ENCODED,
'trace' => 1,
);
$client = new nusoap_client('http://demo.arzoo.com/ArzooWS/services/DOMFlightAvailability?wsdl', $clientinfo);
//print_r($client);
$result = $client->call('getAvailability', array($Request));
echo"<pre>";
print_r($result);
$clientInfo =simplexml_load_string(utf8_encode($result));
$flight = $clientInfo->Response__Depart->OriginDestinationOptions->OriginDestinationOption;
$error =$clientInfo->error__tag;
//echo $error;
var_dump($flight);
//exit;
//echo"<pre>";
//print_r($result);
//ECHO $error;
?>
Since they are mentioned standard SOAP client and you are sending the cURL request to the Server that will never give any response.
$location_URL = "http://xx.xxx.xx.xxx/ArzooWS/services/DOMFlightAvailability";
$action_URL ="http://com.arzoo.flight.avail";
$client = new SoapClient('http://xx.xxx.xx.xxx/ArzooWS/services/DOMFlightAvailability?wsdl', array(
'soap_version' => SOAP_1_1,
'location' => $location_URL,
'uri' => $action_URL,
'style' => SOAP_RPC,
'use' => SOAP_ENCODED,
'trace' => 1,
));
try
{
$result = $client->__call('getAvailability',array($req_int));
$response= htmlentities($result);
}
You can use SOAP1.1 Request.

Categories