Problem Solved
I have successfully obtained an OAuth2 token with proper scopes for accessing Google Calendar Events and Google Contacts (-edit- with both APIs enabled on Google Developers console), using the php client library. I am able to access events with the client using the service however there is no service for contacts.
How can I manually send an authorized GET request to the Google Contacts API (GData-ver:3.0) with the OAuth2 token as referenced here?
This is in Joomla 3.x but I'm working directly with php.
$retrieveURL = 'https://www.google.com/m8/feeds/contacts/default/full?access_token=' . urlencode($tokens->access_token) . '&v=3.0';
$cURL = curl_init();
curl_setopt($cURL, CURLOPT_URL, $retrieveURL);
curl_setopt($cURL, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($cURL, CURLINFO_HEADER_OUT, 1);
curl_setopt($cURL, CURLOPT_SSL_VERIFYPEER, false);
$gContacts = curl_exec($cURL);
if ($errno = curl_errno($cURL)) {
$error_message = curl_strerror($errno);
echo("cURL error ({$errno}):\n {$error_message}:\n ");
var_dump(curl_getinfo($cURL, CURLINFO_HEADER_OUT));
}
var_dump($gContacts);
return $gContacts;
The response from Google:
401. That's an error. There was an error in your request. That's all we know.
Access token cannot be used as a parameter on your http GET request. You need to set them up in your web application. How to do that can be found here.
GET request of https://www.google.com/m8/feeds/contacts/default/full?v=3.0 should be able to work once the set up is done
I figured out how to do it using Google_Http_Request object from the PHP client library.
$retrieveURL = 'https://www.google.com/m8/feeds/contacts/default/full?alt=json';
$httpRequest = new Google_Http_Request($retrieveURL, 'GET');
/**
* this will first check and renew access_token if needed
* then sets the access token in the request header
**/
$client->getAuth()->sign($httpRequest);
/**
* modify the request to work with Google Contacts API v3.0
**/
$httpRequest->setBaseComponent('https://www.google.com');
$httpRequest->setRequestHeaders(['Gdata-version' => '3.0']));
/**
* Send the request and assign the response
**/
$response = $client->getIo()->makeRequest($httpRequest);
// replace problematic '$'s with 'G's in json response string
$responseString = implode('G', explode('$', $response->getResponseBody()));
$responseBody = json_decode($responseString);
$responseArray = $responseBody->feed->entry;
foreach ((array)$responseArray as $entry) {
$gdName = (isset($entry->gdGname->gdGfullName->Gt)) ? $entry->gdGname->gdGfullName->Gt : 'No Name';
$gdFName = (isset($entry->gdGname->gdGgivenName->Gt)) ? $entry->gdGname->gdGgivenName->Gt : 'No First Name';
$gdLName = (isset($entry->gdGname->gdGfamilyName->Gt)) ? $entry->gdGname->gdGfamilyName->Gt : 'No Last Name';
$gdEmail = (isset($entry->gdGemail[0]->address)) ? $entry->gdGemail[0]->address : 'No Email Address';
$gdPhone = (isset($entry->gdGphoneNumber[0]->Gt)) ? $entry->gdGphoneNumber[0]->Gt : 'No Phone Number';
$gdOrgName = (isset($entry->gdGorganization[0]->gdGorgName->Gt)) ? $entry->gdGorganization[0]->gdGorgName->Gt : 'No Company Name';
$output[] = [
"name" => $gdName,
"first_name" => $gdFName,
"last_name" => $gdLName,
"email" => $gdEmail,
"phone" => $gdPhone,
"company" => $gdOrgName,
];
}
$app = $this->app;
// store the contact and redirect to view
$_SESSION["gdContacts"] = $output;
$app->redirect(JRoute::_('web/content/google/contacts'));
Related
I am trying to implement a call back method in PHP. I am successfully calling an instagram API to authorise the user but I do not know how to capture the token after the user authorises.
Below is my code:
public function oAuthBasic()
{
$instagramBasic = new InstagramBasicDisplay([
'appId' => 'xxx',
'appSecret' => 'xxx',
'redirectUri' => 'xxx'
]);
session()->forget('instagramErrorMessage');
$faceBookLoginUrl = $instagramBasic->getLoginUrl();
return response()->json(['redirectUrl' => $faceBookLoginUrl]);
}
This successfully brings up the sign in pop up. However after authorisation, how can I capture the user access token?
Any help is greatly appreciated.
I managed to fix the issue with the help of #CBore's comment:
Created a new route in my web.php
Route::get('linkinstagramBasic','InstagramController#linkBasic')->name('instagram.linkBasic');
Included the URL under Valid OAuth Redirect URIs on facebook app settings page.
Finally wrote the callback:
/*
* Function that works as the call back after Instagram basic display api authorisation
* Get the code and call access_token API
* AUTHOR : DON
* DATE : 12/10/2022
*/
public function linkBasic(InstagramLinkRequest $instagramRequest) {
if (isset($_GET['code'])) {
// Get the OAuth callback code
$code = $_GET['code'];
$ig_atu = "https://api.instagram.com/oauth/access_token";
$ig_data = [];
$ig_data['client_id'] = Config::get('instagram_basic.app_id');
$ig_data['client_secret'] = Config::get('instagram_basic.app_secret');
$ig_data['grant_type'] = 'authorization_code';
$ig_data['redirect_uri'] = Config::get('instagram_basic.redirect_uri');
$ig_data['code'] = $code;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $ig_atu);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($ig_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$ig_auth_data = curl_exec($ch);
curl_close($ch);
$ig_auth_data = json_decode($ig_auth_data, true);
dd($ig_auth_data);
//$accessTok = $ig_auth_data['access_token'];
//$UID = $ig_auth_data['user_id'];
//echo "<script>window.close();</script>";
}
}
i want detect post id of new post in telegram channel with telegram bot that is admin in channel and forward new post to other channel that I have already specified
i tried to get id of post that bot forwarded to channel with this php code below
function bot($method, $datas = [])
{
$url = "https://api.telegram.org/bot" . API_KEY . "/" . $method;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $datas);
$res = curl_exec($ch);
if (curl_error($ch)) {
var_dump(curl_error($ch));
} else {
return json_decode($res);
}
}
$msg_id = bot('ForwardMessage', [
'chat_id' => $channel,
'from_chat_id' => $chat_id,
'message_id' => $message_id
])->result->message_id;
but i need php code that can chek channel if new post send from other admins, robot find post id and forward post to other channel that I have already specified in two channels that robot is admin
i read api bot of telegram there says about how can get post channel in this link: https://core.telegram.org/bots/api#update
in my bot i use setwebhook, which one of $channel_post or $chid is correct?
$update = json_decode(file_get_contents('php://input'));
$channel_post = $update->message->channel_post;
$chid = $update->channel_post->message->message_id;
follow this code :
if( isset($update->channel_post) && $update->channel_post )
{
if(isset($update->channel_post->text) && $update->channel_post->text)
{
$apiToken = "my_bot_api_token";
$data = [
'chat_id' => '#targetChannel',
'text' => $update->channel_post->text
];
file_get_contents("https://api.telegram.org/bot$apiToken/sendMessage?" . http_build_query($data) );
}
}
The access_token acquired by Socialite (via Socialite::driver(self::PROVIDER)->user() has a limited time validity. For Google it's an hour.
I'm able to get refresh_token by changing the redirect call to:
Socialite::driver(self::PROVIDER)->stateless()->with([
'access_type' => 'offline',
])->redirect()
For an hour I'm able to read user data based on access_token by calling
// $token = read_stored_access_token()
\Socialite::driver(self::PROVIDER)->userFromToken($accessToken);
After an hour, when the token gets invalid, the Google API starts returning 401 Unauthorized and Socialite propagates this out:
(1/1) ClientException
Client error: `GET https://www.googleapis.com/plus/v1/people/me?prettyPrint=false` resulted in a `401 Unauthorized` response:
{"error":{"errors":[{"domain":"global","reason":"authError","message":"Invalid Credentials","locationType":"header","loc (truncated...)
Now with the refresh_token, I should be able to easily refresh the access_token. But I cannot find a mention in Socialite docs or source code which would allow me to do that.
Is really the only way how to accomplish this use Google's API library and do this manually? Doesn't it kill the whole idea of using Socialite?
Note: I'm trying to avoid to call redirect() again as it might force user to pick one of his Google accounts every hour which is annoying.
Thanks!
Here is the way I saving user from Socialite with offline access:
$newUser = new User;
$newUser->name = $user->name;
$newUser->email = $user->email;
$newUser->google_id = $user->id;
$newUser->google_token = $user->token;
$newUser->token_expires_at = Carbon::now()->addSeconds($user->expiresIn);
$newUser->google_refresh_token = $user->refreshToken;
$newUser->avatar = $user->avatar;
$newUser->avatar_original = $user->avatar_original;
$newUser->save();
And here is my solution for token refreshing. I made it via creating accessor for token attribute in my User model:
/**
* Accessor for google token of the user
* Need for token refreshing when it has expired
*
* #param $token
*
* #return string
*/
public function getGoogleTokenAttribute( $token ) {
//Checking if the token has expired
if (Carbon::now()->gt(Carbon::parse($this->token_expires_at))) {
$url = "https://www.googleapis.com/oauth2/v4/token";
$data = [
"client_id" => config('services.google.client_id'),
"client_secret" => config('services.google.client_secret'),
"refresh_token" => $this->google_refresh_token,
"grant_type" => 'refresh_token'
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/x-www-form-urlencoded']);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
$result = curl_exec($ch);
$err = curl_error($ch);
curl_close($ch);
if ($err) {
return $token;
}
$result = json_decode($result, true);
$this->google_token = isset($result['access_token']) ? $result['access_token'] : "need_to_refresh";
$this->token_expires_at = isset($result['expires_in']) ? Carbon::now()->addSeconds($result['expires_in']) : Carbon::now();
$this->save();
return $this->google_token;
}
return $token;
}
return Socialite::driver('google')
->scopes()
->with(["access_type" => "offline", "prompt" => "consent select_account"])
->redirect();
By default refresh_token is returned only on first authorization, by adding "prompt" => "consent select_account" we force it to be returned every time.
I am creating an API request (GET bucket) for the storage API and one of the required parameter is the "Authorization" header.
Please note that I am using a Service Account to access the API.
I followed the document https://developers.google.com/accounts/docs/OAuth2ServiceAccount to get the access token for the "Authorization" header so I could send an authorized request to their REST API. The problem is I am always getting the "invalid_grant" error.
Use this code to check it out:
<?php
error_reporting(E_ERROR);
const CLIENT_ID = 'XXXXXXXXXXXX.apps.googleusercontent.com';
const SERVICE_ACCOUNT = 'XXXXXXXXXXXX#developer.gserviceaccount.com';
const KEY_FILE = 'XXX.p12';
function get_oauth_access_token()
{
$header[alg] = 'RS256';
$header[typ] = 'JWT';
$header = urlencode(base64_encode(utf8_encode(json_encode($header))));
$assertion_time = time();
$claim[iss] = CLIENT_ID; //also tried SERVICE_ACCOUNT here, no improvement
$claim[scope] = 'https://www.googleapis.com/auth/devstorage.read_only';
$claim[aud] = 'https://accounts.google.com/o/oauth2/token';
$claim[exp] = $assertion_time + 3600;
$claim[iat] = $assertion_time;
$claim = urlencode(base64_encode(utf8_encode(json_encode($claim))));
$data = $header . '.' . $claim;
$p12 = file_get_contents(KEY_FILE);
$cert = array();
openssl_pkcs12_read($p12, $cert, 'notasecret');
$priv_key_id = openssl_get_privatekey($cert[pkey]);
openssl_sign($data, $signature, $priv_key_id, 'sha256');
$signature = urlencode(base64_encode($signature));
$assertion = $data . '.' . $signature;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://accounts.google.com/o/oauth2/token');
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, array('grant_type'=>'assertion',
'assertion_type'=>'http://oauth.net/grant_type/jwt/1.0/bearer',
'assertion'=>$assertion));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($ch);
$error = curl_error($ch);
curl_close($ch);
var_dump($result);
var_dump($error);
}
get_oauth_access_token();
Is there anything wrong in this code that causes the "invalid_grant" error?
Your problem might be the time of your server.
I faced a problem with Google Analytics: all my code was correct but the time of my server was some seconds in the future compared with Google's time. You can delay your server's time a few minutes to do a test.
If it works you can use NTP for example, to keep the server clock correct.
Here's a simple PHP program illustrating use of a service account with the Google Cloud Storage RESTful HTTP interface. I've tested this code with a service account and it seems to work fine. Let me know if you have any further questions.
<?php
require_once 'apiClient.php';
// Define constants.
const CLIENT_ID = 'YOUR_CLIENT_ID_GOES_HERE';
const SERVICE_ACCOUNT_NAME = 'YOUR_SERVICE_ACCOUNT_NAME_GOES_HERE';
const KEY_FILE = 'key.p12';
const BUCKET = 'marc-us';
// Obtain OAuth 2.0 access token for service account.
$client = new apiClient();
$key = file_get_contents(KEY_FILE);
$client->setAssertionCredentials(new apiAssertionCredentials(
SERVICE_ACCOUNT_NAME,
array('https://www.googleapis.com/auth/devstorage.full_control'),
$key)
);
$client::$auth->refreshTokenWithAssertion();
$json = $client->getAccessToken();
$accessToken = json_decode($json)->access_token;
// Add access token to Authorization header of HTTP request.
$ch = curl_init('http://commondatastorage.googleapis.com/' . BUCKET);
curl_setopt($ch, CURLOPT_HTTPHEADER,
array('Authorization: OAuth ' . $accessToken));
$resp = curl_exec($ch);
curl_close($ch);
// Display results.
print '<h2>Response:</h2><pre>' . $resp . '</pre>';
?>
The code in my last response works fine for me so I suspect you're facing an environmental problem. At any rate, I consulted with the owner of the Google PHP client library and he provided a better way to refresh the access token without resorting to calls to the internal refreshTokenWithAssertion() method. He suggested this technique:
$req = new apiHttpRequest(YOUR_URL);
$val = $client->getIo()->authenticatedRequest($req);
The call to authenticatedRequest() takes care of refreshing the access token (with assertion credentials if they're set) as needed. I modified the code above to use this approach and it works fine for me. Note that both the old and new versions work for me so there's no functional difference but I think the new version is better because it avoids the internal call, uses the Google PHP client lib instead of curl to execute the request and results in much shorter, simpler code.
<?php
require_once 'apiClient.php';
// Define constants.
const SERVICE_ACCOUNT_NAME = 'YOUR_SERVICE_ACCOUNT_NAME';
const KEY_FILE = 'key.p12';
const BUCKET = 'marc-us';
// Obtain service account credentials assertion.
$client = new apiClient();
$key = file_get_contents(KEY_FILE);
$client->setAssertionCredentials(new apiAssertionCredentials(
SERVICE_ACCOUNT_NAME,
array('https://www.googleapis.com/auth/devstorage.full_control'),
$key)
);
// Create HTTP request, authorize and execute.
$req = new apiHttpRequest('http://commondatastorage.googleapis.com/' . BUCKET);
$resp = $client::getIo()->authenticatedRequest($req);
// Display results.
print '<h2>Response:</h2>' . $resp->getResponseBody();
?>
I had the same error, but with Java. My problem was that the time in seconds to exp and iat was in GMT-5 and the API google Auth is in GMT. Then I changed the time to GMT value and all is OK.
Can you tell me with steps how can I do a like button in my site (php) to like a page photo on facebook?
I know that I have to use the GRAPH API and have to do the POST via HTTP to /likes .. but I dont know how can I do it with PHP code.
Somebody have an example?
Thank you
As long as you have obtained the publish_stream permission from the user you can like any photo you need to. If you are attempting to like the photo as a page be sure you have an access_token for the page (obtained via the /accounts connection on the user account).
Once you have the access token the like is as simple as issuing an HTTP POST to a URL that looks similar to this:
https://graph.facebook.com/PHOTO_ID/likes?access_token=ACCESS_TOKEN
Photo_ID = Photo ID in Facebook
Access_Token = Access token obtained from Facebook with the publish_stream permission.
UPDATE
PHP Sample Code based on PHP Form CURL Post
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://graph.facebook.com/PHOTO_ID/likes");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, true);
$data = array(
'Access_Token' => 'token_value'
);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$output = curl_exec($ch);
$info = curl_getinfo($ch);
curl_close($ch);
I would check this though as I'm not sure how accurate it is since I don't normally code PHP. In any manner, the post should be a raw HTTP POST request.
Fabio here is the php post snippet that i was able to get working. Snippet includes a curl to get application access token and the api post to like an object, in this case a post on my app.
The Sample post is here: Shows the post to be liked
https://shawnsspace.com/plugins/TimeLinePost.php?pageid=135669679827333&postid=135669679827333_151602784936066&type=feed&fh=750
The Like Page is here: Should Return whether the user likes or not, or login if not connected.
https://shawnsspace.com/plugins/TheLike.php?postid=135669679827333_151602784936066
Getting the Application Access Token.
function GetCH(){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://graph.facebook.com/oauth/access_token?client_id=YOUR_APP_ID&client_secret=YOUR_APP_SECRET&grant_type=client_credentials");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
curl_setopt($ch,CURLOPT_CONNECTTIMEOUT_MS,20000);
if(substr($url,0,8)=='https://'){
// The following ensures SSL always works. A little detail:
// SSL does two things at once:
// 1. it encrypts communication
// 2. it ensures the target party is who it claims to be.
// In short, if the following code is allowed, CURL won't check if the
// certificate is known and valid, however, it still encrypts communication.
curl_setopt($ch,CURLOPT_HTTPAUTH,CURLAUTH_ANY);
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,false);
}
$sendCH = curl_exec($ch);
curl_close($ch);
return $sendCH;
};
$app_access_token = GetCH();
Looking in url for postid parameter then liking id
if($_GET['postid']){
$postid = $_GET['postid'];
}else{
$postid = '135669679827333_151602784936066';
}
if($user){
$pageLike = $facebook->api('/'.$postid.'/likes?access_token='.$access_token.'&method=post', 'POST');
}
You can get permissions by building an array of perms for login url. below i am requesting read_stream,publish_stream,publish_actions,offline_access in the scope for permissions.
NOTE: app access token needed for logout url.
<?php
$url = (!empty($_SERVER['HTTPS'])) ? 'https://'.$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'] : 'http://'.$_SERVER['SERVER_NAME'].$_SERVER['REQUEST_URI'];
require './src/facebook.php';
$facebook = new Facebook(array(
'appId' => 'APPID',
'secret' => 'APP-SECRET',
'cookie' => true, // enable optional cookie support
));
$user = $facebook->getUser();
if ($user) {
try {
// Proceed knowing you have a logged in user who's authenticated.
$user_profile = $facebook->api('/me');
//$pageInfo = $facebook->api('/'.$pageid.'?access_token='.$_SESSION['fb_112104298812138_access_token].');
//$pageInfoUser = $user_profile[id];
} catch (FacebookApiException $e) {
error_log($e);
$user = null;
}
}
/* */
if ($user) {
$logoutUrl = $facebook->getLogoutUrl();
} else {
$params = array(
scope => 'read_stream,publish_stream,publish_actions,offline_access',
redirect_uri => $url
);
$loginUrl = $facebook->getLoginUrl($params);
}
$access_token = $_SESSION['fb_135669679827333_access_token'];
?>
<?php
if(!$user){
echo ' : Login ';
}else{
echo 'Logout';
}
?>