Update twitter profile image using OAuth - php

I'm trying to get twitter update_profile_image to work using OAuth. I was using curl with regular authentication and everything was working fine, but I switched to OAuth using this library, and now everything except update_profile_image works.
I read something about twitter OAuth having problems with multipart data, but that was a while ago and the plugin is supposed to have dealt with that issue.
My working regular authentication with curl code
$url = 'http://api.twitter.com/1/account/update_profile_image.xml';
$uname = $_POST['username'];
$pword = $_POST['password'];
$img_path = 'xxx';
$userpwd = $uname . ':' . $pword;
$img_post = array('image' => '#' . $img_path . ';type=image/jpeg',
'tile' => 'true');
$format = 'xml'; //alternative: json
$message = 'Test update with a random num'.rand();
$opts = array(CURLOPT_URL => $url,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $img_post,
CURLOPT_HTTPAUTH => CURLAUTH_ANY,
CURLOPT_USERPWD => $userpwd,
CURLOPT_HTTPHEADER => array('Expect:'),
CURLINFO_HEADER_OUT => true);
$ch = curl_init();
curl_setopt_array($ch, $opts);
$response = curl_exec($ch);
$err = curl_error($ch);
$info = curl_getinfo($ch);
curl_close($ch);
My current OAuth code [I had to cut it down, so do not minor look for syntax errors]
include 'EpiCurl.php';
include 'EpiOAuth.php';
include 'EpiTwitter.php';
include 'secret.php';
$twitterObj = new EpiTwitter($consumer_key, $consumer_secret);
$twitterObj->setToken($_GET['oauth_token']);
$token = $twitterObj->getAccessToken();
$twitterObj->setToken($token->oauth_token, $token->oauth_token_secret);
try{
$img_path = 'xxx';
//$twitterObj->post_accountUpdate_profile_image(array('#image' => "#".$img_path));
$twitterObj->post('/account/update_profile_image.json', array('#image' => "#".$img_path));
$twitterObj->post_statusesUpdate(array('status' => 'This is my new status:'.rand())); //This works
$twitterInfo= $twitterObj->get_accountVerify_credentials();
echo $twitterInfo->responseText;
}catch(Exception $e){
echo $e->getMessage();
}
I've been trying to figure this out for a while, ANY help would be greatly appreciated. I'm not in any way tied to this library, so feel free to recommend others.

The version of the library I was using was outdated. Once I updated, I had to deal with a couple of other issues including 401 error due to a wrong server time, and now everything works fine. Printing out the $response->responseText helps a lot.

Related

Is it possible to use a server (using PHP) to send an IOS Push Notification?

I am trying to send push notifications using a web server and PHP. I have tried to use the code from an answer to a similar question (https://stackoverflow.com/a/45676540/11051450), but I cannot make it work for some reason. I have figured that this might be because this code was not ment for a web server. In the linked answer they also emphesize that you need curl version 7.38.0, but I think my web server only have curl version 7.29.0. This is the code that I have tried to use:
<?php
$keyfile = 'AuthKey_AABBCC1234.p8'; # <- Your AuthKey file
$keyid = 'AABBCC1234'; # <- Your Key ID
$teamid = 'AB12CD34EF'; # <- Your Team ID (see Developer Portal)
$bundleid = 'com.company.YourApp'; # <- Your Bundle ID
$url = 'https://api.development.push.apple.com'; # <- development url, or use http://api.push.apple.com for production environment
$token = 'e2c48ed32ef9b018........'; # <- Device Token
$message = '{"aps":{"alert":"Hi there!","sound":"default"}}';
$key = openssl_pkey_get_private($keyfile);
$header = ['alg'=>'ES256','kid'=>$keyid];
$claims = ['iss'=>$teamid,'iat'=>time()];
$header_encoded = base64($header);
$claims_encoded = base64($claims);
$signature = ''; //should I put a password here?
openssl_sign($header_encoded . '.' . $claims_encoded, $signature, $key, 'sha256');
$jwt = $header_encoded . '.' . $claims_encoded . '.' . base64_encode($signature);
// only needed for PHP prior to 5.5.24
if (!defined('CURL_HTTP_VERSION_2_0')) {
define('CURL_HTTP_VERSION_2_0', 3);
}
$http2ch = curl_init();
curl_setopt_array($http2ch, array(
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2_0,
CURLOPT_URL => "$url/3/device/$token",
CURLOPT_PORT => 443,
CURLOPT_HTTPHEADER => array(
"apns-topic: {$bundleid}",
"authorization: bearer $jwt"
),
CURLOPT_POST => TRUE,
CURLOPT_POSTFIELDS => $message,
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_TIMEOUT => 30,
CURLOPT_HEADER => 1
));
$result = curl_exec($http2ch);
if ($result === FALSE) {
throw new Exception("Curl failed: ".curl_error($http2ch));
}
$status = curl_getinfo($http2ch, CURLINFO_HTTP_CODE);
echo $status;
function base64($data) {
return rtrim(strtr(base64_encode(json_encode($data)), '+/', '-_'), '=');
}
?>
So my question is as the title explains if you are able to send a push notification to a IOS device using a web sever and if can give me a hint to how I should do it/is doing wrong?

Apple Push Notifications with p8 file working on my Mac, but doesn't work on Linux server

I'm trying to switch the APNS for my apps from p12 over to the new p8 format. It works fine on my Mac and sends notifications with no problems. However, when I setup the same config on my Amazon Linux servers, I get nothing. I'm having a rough time figuring out what is different. This is the function I am using. It works fine when run on my local host, but not on the remote servers. Also, note that the $result from curl_exec($http2ch) is coming back as false for the remote servers.
public function sendNotification($token, $message, $badge, $env){
$keyfile = 'AuthKey_mykey.p8';
$keyid = 'my key id';
$teamid = 'my team id';
$bundleid = 'org.tciweb.y2go';
if ($env == 'dev') {
$url = 'https://api.development.push.apple.com';
} else {
$url = 'https://api.push.apple.com';
}
$payload = '{"aps":{"alert":"'.$message.'","sound":"default"}}';
$key = openssl_pkey_get_private('file://'.$keyfile);
$header = ['alg'=>'ES256','kid'=>$keyid];
$claims = ['iss'=>$teamid,'iat'=>time()];
$header_encoded = $this->base64($header);
$claims_encoded = $this->base64($claims);
$signature = '';
openssl_sign($header_encoded . '.' . $claims_encoded, $signature, $key, 'sha256');
$jwt = $header_encoded . '.' . $claims_encoded . '.' . base64_encode($signature);
// only needed for PHP prior to 5.5.24
if (!defined('CURL_HTTP_VERSION_2_0')) {
define('CURL_HTTP_VERSION_2_0', 3);
}
$http2ch = curl_init();
curl_setopt_array($http2ch, array(
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_2_0,
CURLOPT_URL => "$url/3/device/$token",
CURLOPT_PORT => 443,
CURLOPT_HTTPHEADER => array(
"apns-topic: {$bundleid}",
"authorization: bearer $jwt"
),
CURLOPT_POST => TRUE,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_RETURNTRANSFER => TRUE,
CURLOPT_TIMEOUT => 30,
CURLOPT_HEADER => 1
));
$result = curl_exec($http2ch);
if ($result === FALSE) {
// error handling
}
$status = curl_getinfo($http2ch, CURLINFO_HTTP_CODE);
echo $status;
return 0;
}
I checked the curl versions... 7.64.1 on my local machine and 7.61.1 on the servers. I also ran "Push Notifications Tester" to verify that the keys and device token work.
I've done a bunch of searching, but am getting really confused. Could this be something to do with HTTP2? Is there an alternative to using CURL? Any suggestions appreciated.
Upgrading PHP to version 7.3 solved the problem. I was originally on version 7.0.

How to send a message to specific connectionId to aws api gateway websockets?

I am using this code to establish a new connection on user device.
var socket = new WebSocket("wss://cdsbxtx2xi.execute-api.us-east-2.amazonaws.com/test");
socket.onmessage = function (event) {
json = JSON.parse(event.data);
connectionId = json.connectionId;
document.cookie = "connection_id="+connectionId;
console.info(json);
}
Suppose from this request I get connectionId CLO5bFP1CYcFSbw=
Another user from another device also established a new connection with connectionId Cs42Fs5s5yuSbc=. Now how can I send a message from user 2 device to user 1?
I already tried this. I don't know this is right way or not but still, i am open for any suggestion.
use Aws\Signature\SignatureV4;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request;
use Aws\Credentials\Credentials;
$client = new GuzzleHttp\Client();
$credentials = new Credentials("XXXXXXXXXX","XXXXXXXX");
$url = "https://cdsbxtx2xi.execute-api.us-east-2.amazonaws.com/test/#connections/CLO5bFP1CYcFSbw=";
$region = 'us-east-2';
$msg['action'] = 'sendmessage';
$msg['data'] = 'hello world';
$msg = json_encode($msg);
$request = new Request('POST', $url, '["json"=>$msg]');
$s4 = new SignatureV4("execute-api", $region);
$signedrequest = $s4->signRequest($request, $credentials);
$response = $client->send($signedrequest);
echo $response->getBody();
This code keeps on loading and finally throws gateway timeout error.
I expect that user 2 should be able to send message to any specific connectionId over wss or https.
I tried https by signing this request but signing doesn't works. I am getting an error with the signing part
After struggling with this problem for the last 3 days finally I found the solution. None of the previously mentioned solutions on StackOverflow was working for me.
This is the correct solution. I hope this will be helpful to someone.
use Aws\Signature\SignatureV4;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request;
use Aws\Credentials\Credentials;
$client = new GuzzleHttp\Client();
$credentials = new Credentials(accessKeyId, secretAccessKey);
$url = "https://xsdsdsd.execute-api.us-east-2.amazonaws.com/test/#connections/CNtBveH2iYcCKrA=";
// CNtBveH2iYcCKrA= is connectionid
$region = 'us-east-2';
$msg['action'] = 'sendmessage';
$msg['data'] = 'hello world';
$msg = json_encode($msg);
$headers = array('Content-Type => application/x-www-form-urlencoded');
$request = new GuzzleHttp\Psr7\Request('POST', $url, ['Content-Type' => 'application/json'], $msg);
$signer = new Aws\Signature\SignatureV4('execute-api', $region);
$request = $signer->signRequest($request, $credentials);
$headers = array('Content-Type => application/x-www-form-urlencoded');
$client = new \GuzzleHttp\Client([ 'headers' => $headers]);
$response = $client->send($request);
$result = $response->getBody();
Hey you can use the Connection URL to send message also.
Connection url : https://{api-id}.execute-api.us-east-1.amazonaws.com/{stage}/#connections
To find go to : Aws console > Api gateway > api > your_api > dashboard their you will find your connection url.
Use php cURL method because its easy and fast as compare to GuzzleHttp
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://{api-id}.execute-api.us-east-1.amazonaws.com/{stage}/#connections/{YOUR_CONNECTION_ID_OF_USER}',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS =>'{"message" : "Hello world"}',
CURLOPT_HTTPHEADER => array(
'Content-Type: application/json'
),
));
$response = curl_exec($curl);
curl_close($curl);
echo $response;

LinkedIn Get Token Access via PHP cUrl Fail

I try to create a register function with linkedIn in my website.
As you know, linkedIn has 2 step authentication to get Access token.
See StackOverflow answer here : Click here
From the thread above,
i try to do the second step with PHP cUrl.
$url = "https://www.linkedin.com/oauth/v2/accessToken?grant_type=authorization_code&code=" . $_GET['code'] . "&redirect_uri=A_URL&client_id=MY_CLIENT_ID&client_secret=SECRET";
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => $url,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_TIMEOUT => 1000,
CURLOPT_HTTPHEADER => array(
"Content-Length: 1000"
)
));
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if (!$err) {
$response = json_decode($response);
} else {
throw new exception(__METHOD__ . '() ' . $err . ', on line : ' . __LINE__);
}
but when the curl_exec() is executed, the page goes to loading infinitely.
I do not know what's my mistake. I try to run the $url manually via the url box, it return a token and expired time successfully. So, i conclude the mistake is in the cUrl.
Any help guys ?,
Thank you very much.
CURLOPT_CUSTOMREQUEST => "GET"
and delete CURLOPT_HTTPHEADER option, It's useless.

Google Admin SDK: You are not authorized to access this API

Since the Google Login Auth is disabled since last week I'm trying to get oAuth 2.0 working with a service account. We want to give users on our internal web application the oppurtunity to set there Out of Office.
I downloaded the lastest Google APIs Client Library for PHP. In the Google Developer Console, I have created a new project for my application and created a Service account credentials. I have also enabled the API service: Admin SDK in the Developer Console.
I have granted the account user ID access to the correct scopes (I think):
When I use the service-account.php example and change the details, I recieve an JSON with an access token, but when I do an CURL request (same as before) to get the e-mail settings from a user, the error "You are not authorized to access this API." occur.
My code:
<?php
include_once "templates/base.php";
require_once realpath(dirname(__FILE__) . '/../src/Google/autoload.php');
$client_id = '124331845-DELETEDPART-hbh89pbgl20citf6ko.apps.googleusercontent.com'; //Client ID
$service_account_name = '124331845-DELETEDPART-89pbgl20citf6ko#developer.gserviceaccount.com'; //Email Address
$key_file_location = 'globaltext-4ce09b20cb73.p12'; //key.p12
$client = new Google_Client();
if (isset($_SESSION['service_token'])) {
$client->setAccessToken($_SESSION['service_token']);
}
$key = file_get_contents($key_file_location);
$cred = new Google_Auth_AssertionCredentials(
$service_account_name,
array('https://apps-apis.google.com/a/feeds/emailsettings/2.0/'),
$key
);
$client->setAssertionCredentials($cred);
if ($client->getAuth()->isAccessTokenExpired()) {
$client->getAuth()->refreshTokenWithAssertion($cred);
}
$aOutput = json_decode($client->getAccessToken());
$strEmailAdresSplit = explode('#', "FIRSTNAME.LASTNAME#DOMAIN.EXTENSION");
$strDomein = $strEmailAdresSplit[1];
$strAlias = $strEmailAdresSplit[0];
$resConnectionJobs = curl_init();
$aHeader = array();
$aHeader[] = 'Authorization: Bearer '.$aOutput->access_token;
$aHeader[] = 'Content-Type: application/atom+xml';
curl_setopt($resConnectionJobs, CURLOPT_URL, "https://apps-apis.google.com/a/feeds/emailsettings/2.0/DOMAIN.EXTENSION/FIRSTNAME.LASTNAME/vacation");
curl_setopt($resConnectionJobs, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($resConnectionJobs, CURLOPT_HTTPHEADER, $aHeader);
curl_setopt($resConnectionJobs, CURLOPT_RETURNTRANSFER, true);
curl_setopt($resConnectionJobs, CURLOPT_HEADER, false);
$oCurlData = curl_exec($resConnectionJobs);
curl_close($resConnectionJobs);
echo $oCurlData;
?>
Are you certain your credentials are OK?
Please try the following procedure to make sure you have the right credentials.
Creating your API keys
Go to the developer's console and follow these steps:
Select your project
Choose menu item "APIs & auth"
Choose menu item "Registered app"
Register an app of type "web application"
Choose one of the following options, depending on what kind of app you're creating. Server side languages should use this option :
Key for server apps (with IP locking)
Getting access token & refresh token
Create a file that contains the following code :
<?php
if (isset($_GET['code'])) {
// try to get an access token
$code = $_GET['code'];
$url = 'https://accounts.google.com/o/oauth2/token';
$params = array(
"code" => $code,
"client_id" => YOUR_CLIENT_ID,
"client_secret" => YOUR_CLIENT_SECRET,
"redirect_uri" => 'http://' . $_SERVER["HTTP_HOST"] . $_SERVER["PHP_SELF"],
"grant_type" => "authorization_code"
);
$ch = curl_init();
curl_setopt($ch, constant("CURLOPT_" . 'URL'), $url);
curl_setopt($ch, constant("CURLOPT_" . 'POST'), true);
curl_setopt($ch, constant("CURLOPT_" . 'POSTFIELDS'), $params);
$output = curl_exec($ch);
$info = curl_getinfo($ch);
curl_close($ch);
if ($info['http_code'] === 200) {
header('Content-Type: ' . $info['content_type']);
return $output;
} else {
return 'An error happened';
}
} else {
$url = "https://accounts.google.com/o/oauth2/auth";
$params = array(
"response_type" => "code",
"client_id" => YOUR_CLIENT_ID,
"redirect_uri" => 'http://' . $_SERVER["HTTP_HOST"] . $_SERVER["PHP_SELF"],
"scope" => "https://www.googleapis.com/auth/plus.me"
);
$request_to = $url . '?' . http_build_query($params);
header("Location: " . $request_to);
}
Now, replace YOUR_CLIENT_ID and YOUR_CLIENT_SECRET with your client ID and client secret.
Make sure your scope is correct. For example, it should be https://www.googleapis.com/auth/analytics if you want to get access to Analytics.
If you run the file, you should get an OAuth2 approval screen.
If you now press Accept, you should get a result that looks like this:
{
"access_token" : YOUR_ACCESS_TOKEN,
"token_type" : "Bearer",
"expires_in" : 3600,
"refresh_token" : YOUR_REFRESH_TOKEN
}
The result may contain additional fields, depending on which scope you're applying for.
Connecting with Google's systems in background
Once you get the above to work, your application needs to implement the following workflow:
1) Check if your input contains a GET parameter named "code". If "code" is present, get a new access token and repeat this step (refresh your page)
If "code" is not present, go to step 2.
2) Check if you have credentials stored for your service. If credentials are present, check if your access token has expired or will expire soon. Then go to step 3. If credentials are not present, go to the auth path of your service to get the auth code and go back to step 1 (make sure Google redirects to your current URL).
3) If refresh is needed, refresh your page and go back to step 1.
If refresh is not needed, you're ready to actually do what you wanted to do in the first place.
Google's PHP library takes care if the oAuth2 flow for you, however. If you're using their library, each of the steps in the 3-step process are taken care of by the library and you should just be able to do whatever you want to do with Google's services straight away. I use this strategy myself in my Google Adwords dashboard.
You can, however, just write your custom library and connect with the service directly. Herebelow is some dev code from a project I wrote a few months ago. While it doesn't work out of the box (since it's a controller that's part of a larger application), it should help you understand the flow that Google's library takes care of under the hood.
namespace Application;
class Controller_API_Google_Youtube extends Controller_API {
public function read() {
$scope = "https://www.googleapis.com/auth/youtube";
$this->doOauth($scope);
}
function doOauth($scope) {
$oauth2Credentials = JSON_File::load(__DIR__ . DIRECTORY_SEPARATOR . 'Config.json');
$paths = array(
'token' => 'https://accounts.google.com/o/oauth2/token',
'auth' => "https://accounts.google.com/o/oauth2/auth"
);
$refreshtime = 300;
if (isset($_GET['code'])) {
// Get access code
$query = $_GET;
unset($query['code']);
if (count($query) > 0) {
$query = '?' . http_build_query($query);
} else {
$query = '';
}
$client = \PowerTools\HTTP_Client::factory(
array(
'maps' => array(
'url' => $paths['token'],
'returntransfer' => 1,
'post' => true,
'postfields' => array(
'code' => $_GET['code'],
"client_id" => $oauth2Credentials['client_id'],
"client_secret" => $oauth2Credentials['client_secret'],
"redirect_uri" => HTTP_PROTOCOL . URL_PATH . $query,
"grant_type" => "authorization_code"
)
)
)
)->execute();
$responses = $client->getResponses();
$response = array_pop($responses);
$info = $response['maps']->getInfo();
$content = $response['maps']->getContent();
if ($info['http_code'] === 200) {
$output = JSON::decode($content);
$oauth2Credentials[$scope] = array();
$oauth2Credentials[$scope]['expires'] = time() + $output['expires_in'];
$oauth2Credentials[$scope]['access_token'] = $output['access_token'];
$oauth2Credentials[$scope]['refresh_token'] = $output['refresh_token'];
file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . 'Config.json', JSON::encode($oauth2Credentials));
header("Location: " . HTTP_PROTOCOL . URL_PATH . $query);
} else {
echo "Something went wrong";
}
} elseif (!isset($oauth2Credentials[$scope])) {
// Get auth code
header("Location: " . $paths['auth'] . '?' . http_build_query(
array(
"response_type" => "code",
"client_id" => $oauth2Credentials['client_id'],
"redirect_uri" => HTTP_PROTOCOL . DOMAIN_PATH,
"scope" => $scope
)
));
} elseif ($oauth2Credentials[$scope]['expires'] - $refreshtime < time()) {
// Refresh access code
$client = \PowerTools\HTTP_Client::factory(
array(
'maps' => array(
'url' => $paths['token'],
'returntransfer' => 1,
'post' => true,
'postfields' => array(
"client_id" => $oauth2Credentials['client_id'],
"client_secret" => $oauth2Credentials['client_secret'],
"refresh_token" => $oauth2Credentials[$scope]['refresh_token'],
"grant_type" => "refresh_token"
)
)
)
)->execute();
$responses = $client->getResponses();
$response = array_pop($responses);
$info = $response['maps']->getInfo();
$content = $response['maps']->getContent();
if ($info['http_code'] === 200) {
$output = JSON::decode($response['maps']->getContent());
$oauth2Credentials[$scope]['expires'] = time() + $output['expires_in'];
$oauth2Credentials[$scope]['access_token'] = $output['access_token'];
file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . 'Config.json', JSON::encode($oauth2Credentials));
$this->read();
} else {
$this->output = array("error" => "Something went wrong");
}
} else {
$this->doSomethinguseful($oauth2Credentials, $scope);
}
return $this;
}
function doSomethinguseful($oauth2Credentials, $scope) {
// https://developers.google.com/youtube/v3/sample_requests?hl=nl
$client = \PowerTools\HTTP_Client::factory(
array(
'maps' => array(
'useragent' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.13) Gecko/20080311 Firefox/2.0.0.13',
'url' => 'https://www.googleapis.com/youtube/v3/channels?part=contentDetails&mine=true',
'returntransfer' => true,
'httpheader' => array(
'Authorization: Bearer ' . $oauth2Credentials[$scope]['access_token'],
'Accept-Encoding: gzip, deflate'
)
)
)
)->execute();
$responses = $client->getResponses();
$response = array_pop($responses);
$content = $response['maps']->getContent();
$this->output = JSON::decode(gzdecode($content));
}
}
It looks like you may be running into a problem I had as well.
The call to Google_Auth_AssertionCredentials actually requires more parameters than you're sending to work with a service account. (At least, it did in my case.)
You need to pass enough parameters to include sub (which user to take actions on account of).
Without that, I always got an access denied. This clearly isn't obvious, since there's even been a function added to the php library, loadServiceAccountJson, which is supposed to set up a service account client connection, but breaks because it doesn't set sub either.
See working code here: Google php client library loadServiceAccountJson broken - fix enclosed

Categories