I'm trying to use Google Calendar API in PHP to access one central account. The idea is that other users can add events to this central account (they don't have to be logged in Google account) so I would like to have permanent access to the account.
Firstly, I've done Quickstart tutorial on Google's webpage. I logged in my account and everything was working for some time. Later, my access token expired and now, $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken()); throws LogicException: refresh token must be passed in or set as part of setAccessToken. I found out that $client->getRefreshToken() returns null.
Here you can see my whole code to get Google Client:
$client = new Google_Client();
$client->setApplicationName('Google Calendar API PHP Quickstart');
$client->setScopes(implode(' ', array(Google_Service_Calendar::CALENDAR) ) );
$client->setAuthConfig(__DIR__ . '/../config/client_secret.json');
$client->setAccessType('offline');
$credentialsPath = __DIR__ . '/../config/calendar-php-quickstart.json';
$accessToken = json_decode(file_get_contents($credentialsPath), true);
$client->setAccessToken($accessToken);
// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}
Any idea what am I doing wrong?
Try using DalmTo's tutorial with regard to Calendar API and service accounts.
This is the refresh token implementation from her guide:
$client->setAssertionCredentials($cred);
if($client->getAuth()->isAccessTokenExpired()) {
$client->getAuth()->refreshTokenWithAssertion($cred);
}
You can test the following code :
// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
// save refresh token to some variable
$refreshTokenSaved = $client->getRefreshToken();
// update access token
$client->fetchAccessTokenWithRefreshToken($refreshTokenSaved);
// pass access token to some variable
$accessTokenUpdated = $client->getAccessToken();
// append refresh token
$accessTokenUpdated['refresh_token'] = $refreshTokenSaved;
// save to file
file_put_contents($credentialsPath, json_encode($accessTokenUpdated));
}
I'll share my code that I finally got to work. I'm using a front end web app to get the initial authorization and then passing that code to a php web service to store in the dbase for later use.
<?php
require_once 'vendor/autoload.php';
/**
class to provide google calendar api functions.
returns information to mobile web app in json format
**/
class Calendar
{
private $client;
private $clientsecretgoogle = '{input secret here}';
private $clientIDgoogle = '{input client ID here}';
private $objDatabase;
private $queryParams;
private $params;
public function __construct($params)
{
$this->client = new Google_Client();
$this->client->setClientId($this->clientIDgoogle);
$this->client->setClientSecret($this->clientsecretgoogle);
$this->client->setAccessType('offline'); //this seems to be important for refresh tokens to update
$this->objDatabase = new Database();//connect to dbase
$this->params=$params;
}
public function getAuthorizationRefreshTokensGoogle($authCode, $userID)
{
/*
Get access_token and refresh token from returned auth code
I'm getting this on the front end and then storing in dbase table
Could have used the basic php library here but this worked so I stuck with it
*/
$oauth_url = 'https://www.googleapis.com/oauth2/v4/token?code=' . $authCode . '&client_secret=' . $this->clientsecretgoogle . '&client_id=' . $this->clientIDgoogle . '&redirect_uri=postmessage&grant_type=authorization_code';
//using curl
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $oauth_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$return = curl_exec($ch);
curl_close($ch);
$return = json_decode($return);
// You can dump the vars to see the object
//var_dump($return);
//insert into table for later retrieval
$strSQL = "insert ignore into tbloauth(oauth_usr_id,oauth_vendor,oauth_token,oauth_refresh_token,oauth_token_expire)VALUES
(?,?,?,?,?)";
//check for existence of refresh token in response
//if not there then replace only the access_token
//if there then replace access, refresh
if(!$return->refresh_token){
$strSQL .= "ON DUPLICATE KEY UPDATE oauth_token=VALUES(oauth_token), oauth_token_expire=VALUES(oauth_token_expire)";
}else{
$strSQL .= "ON DUPLICATE KEY UPDATE oauth_token=VALUES(oauth_token),oauth_refresh_token=VALUES(oauth_refresh_token), oauth_token_expire=VALUES(oauth_token_expire)";
}
//translate expire time NOW + 3600
$expire = (time() + $return->expires_in);
if (!$return->refresh_token) {
$return->refresh_token = '';
}
$this->queryParams = array($userID, 'google', $return->access_token, $return->refresh_token, $expire);
$this->objDatabase->insertQuery($strSQL, $this->queryParams);
//you should now have a record now containing the access_token,refresh token, expire date
}
/*
List calendars
*/
function listCalendarsGoogle()
{
//1. Get tokens from database for the current user
$result=$this->getUserAccessToken($this->params['usr_id'],'google');
//2. Set access token with returned results
$this->client->setAccessToken(json_encode($result[0]));
//check for expired token and refresh if necessary
if($this->client->isAccessTokenExpired()) {
$this->client->refreshToken($result[0]['oauth_refresh_token']);
$access_token = $this->client->getAccessToken();
$strSQL="update tbloauth set oauth_token=? where oauth_vendor='google' and oauth_usr_id=? limit 1";
$this->queryParams=array($access_token,$this->params['usr_id']);
$this->objDatabase->updateQuery($strSQL,$this->queryParams);
}
//3.init calendar
$calendar = new Google_Service_Calendar($this->client);
//4. list calendars of primary
$calendarList = $calendar->calendarList->listCalendarList('primary');
return json_encode($calendarList);
}
/**
get access token for vendor(google in this case) using usr_id of user
which is passed to the class from the web app
*/
public function getUserAccessToken($usr_id,$vendor){
$strSQL="select tbloauth.*,oauth_token as access_token,oauth_refresh_token as refresh_token from tbloauth where oauth_usr_id=? and oauth_vendor=? ";
$this->queryParams=array($usr_id,$vendor);
//echo $this->objDatabase->parms($strSQL,$this->queryParams);
$aResult=$this->objDatabase->selectQuery($strSQL,$this->queryParams);
$aResult[0]['client_id']=$this->clientIDgoogle;
$aResult[0]['client_secret']=$this->clientsecretgoogle;
return $aResult;
}
/**
Insert Event into google using event info posted by web app
*/
public function insertEventGoogle(){
$result=$this->getUserAccessToken($this->params['usr_id'],'google');
$this->client->setAccessToken(json_encode($result[0]));
//check if expired
//if expired then fetch new token with current token
//update dbase with new token
//this would be cleaner separated into another method
//called before each list or insert calendar function
if($this->client->isAccessTokenExpired()) {
$this->client->refreshToken($result[0]['oauth_refresh_token']);
$access_token = $this->client->getAccessToken();
$strSQL="update tbloauth set oauth_token=? where oauth_vendor='google' and oauth_usr_id=? limit 1";
$this->queryParams=array($access_token,$this->params['usr_id']);
$this->objDatabase->updateQuery($strSQL,$this->queryParams);
}
//init calendar
$calendar = new Google_Service_Calendar($this->client);
//event info
$event = new Google_Service_Calendar_Event(array(
'summary' => $this->params['event_name'],
'location' => $this->params['event_location'],
'description' => $this->params['event_desc'],
'start' => array(
'dateTime' => date(DATE_ISO8601, strtotime($this->params['event_start_date'].' '.$this->params['event_start_time'])),
'timeZone' => 'America/New_York',
),
'end' => array(
'dateTime' => date(DATE_ISO8601, strtotime($this->params['event_end_date'].' '.$this->params['event_end_time'])),
'timeZone' => 'America/New_York',
),
'reminders' => array(
'useDefault' => FALSE,
'overrides' => array(
array('method' => 'email', 'minutes' => 24 * 60),
array('method' => 'popup', 'minutes' => 10),
),
),
));
//calendar id - default primary
$calendarId = 'primary';
//insert event
$event = $calendar->events->insert($calendarId, $event);
//return confirm msg
$msg=array('msg'=>'Event Created: '.$this->params['event_name']);
return json_encode($msg);
}
}
Hope that helps someone. The documentation was difficult to sort through.
Related
I would like to share and add events to my own google calendar from my website.
So, I don't want the oauth prompt. Because users who visite my website can also add event to my calendar.
I think I did the right thing but I have this error : Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested
So, I don't sur if I need a paid workspace account to do that.
If not, I can't understand where I failed ?
here is my code :
$client = new Google\Client();
$client->setAuthConfig('./secret.json');
$client->setApplicationName('app name');
$client->addScope(Google\Service\Calendar::CALENDAR_EVENTS);
$client->setSubject('mail#gmail.com');
$client->setAccessType('offline');
$service = new Google\Service\Calendar($client);
$event = new Google\Service\Calendar\Event(array(
'summary' => 'summary',
'location' => 'street bla bla',
'description' => 'first event',
'start' => array(
'dateTime' => '2021-11-30T10:00:00.000-05:00',
'timeZone' => 'Europe/Brussels',
),
'end' => array(
'dateTime' => '2021-11-30T10:00:00.000-05:00',
'timeZone' => 'Europe/Brussels',
)
));
$service->events->insert('calendar_id', $event);
Thanks
The issue is that you are trying to set the subject or delegate to a normally Gmail email address $client->setSubject('mail#gmail.com');
Service accounts only work with Google workspace domain accounts and only after you have properly set up delegation to a user on the domain.
Im sorry to tell you that you will not be able to use a service account with your gmail email address.
What you could do is run the code from the Sample project which uses an installed application and stores the refresh token. This code will be single user. You will have to monitor it a little as refresh tokens can expire although its not that common once your project is set into prodctuion.
require __DIR__ . '/vendor/autoload.php';
if (php_sapi_name() != 'cli') {
throw new Exception('This application must be run on the command line.');
}
/**
* Returns an authorized API client.
* #return Google_Client the authorized client object
*/
function getClient()
{
$client = new Google_Client();
$client->setApplicationName('Google Calendar API PHP Quickstart');
$client->setScopes(Google_Service_Calendar::CALENDAR_READONLY);
$client->setAuthConfig('credentials.json');
$client->setAccessType('offline');
$client->setPrompt('select_account consent');
// Load previously authorized token from a file, if it exists.
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
$tokenPath = 'token.json';
if (file_exists($tokenPath)) {
$accessToken = json_decode(file_get_contents($tokenPath), true);
$client->setAccessToken($accessToken);
}
// If there is no previous token or it's expired.
if ($client->isAccessTokenExpired()) {
// Refresh the token if possible, else fetch a new one.
if ($client->getRefreshToken()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
} else {
// Request authorization from the user.
$authUrl = $client->createAuthUrl();
printf("Open the following link in your browser:\n%s\n", $authUrl);
print 'Enter verification code: ';
$authCode = trim(fgets(STDIN));
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);
// Check to see if there was an error.
if (array_key_exists('error', $accessToken)) {
throw new Exception(join(', ', $accessToken));
}
}
// Save the token to a file.
if (!file_exists(dirname($tokenPath))) {
mkdir(dirname($tokenPath), 0700, true);
}
file_put_contents($tokenPath, json_encode($client->getAccessToken()));
}
return $client;
}
// Get the API client and construct the service object.
$client = getClient();
$service = new Google_Service_Calendar($client);
// Print the next 10 events on the user's calendar.
$calendarId = 'primary';
$optParams = array(
'maxResults' => 10,
'orderBy' => 'startTime',
'singleEvents' => true,
'timeMin' => date('c'),
);
$results = $service->events->listEvents($calendarId, $optParams);
$events = $results->getItems();
if (empty($events)) {
print "No upcoming events found.\n";
} else {
print "Upcoming events:\n";
foreach ($events as $event) {
$start = $event->start->dateTime;
if (empty($start)) {
$start = $event->start->date;
}
printf("%s (%s)\n", $event->getSummary(), $start);
}
}
i need to add events to the google calendar with the api, on my localhost i made it work, however i cant remember where the token comes from, can you guys remind me this? the toke.json file
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
// If you've used composer to include the library
require 'vendor/autoload.php';
/*
if (php_sapi_name() != 'cli') {
throw new Exception('This application must be run on the command line.');
}
*/
/**
* Returns an authorized API client.
* #return Google_Client the authorized client object
*/
function getClient()
{
$client = new Google_Client();
$client->setApplicationName('Calendar API Test');
$client->setScopes( ['https://www.googleapis.com/auth/calendar'] );
// $client->setScopes(Google_Service_Calendar::CALENDAR_READONLY);
$client->setAuthConfig('credentials.json');
$client->setAccessType('offline');
$client->setPrompt('select_account consent');
// Load previously authorized token from a file, if it exists.
// and refresh tokens, and is created automatically when the authorization flow completes for the first time.
$tokenPath = 'token.json';
if (file_exists($tokenPath)) {
$accessToken = json_decode(file_get_contents($tokenPath), true);
$client->setAccessToken($accessToken);
}
// If there is no previous token or it's expired.
if ($client->isAccessTokenExpired()) {
// Refresh the token if possible, else fetch a new one.
if ($client->getRefreshToken()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
} else {
// Request authorization from the user.
$authUrl = $client->createAuthUrl();
printf("Open the following link in your browser:\n%s\n", $authUrl);
print 'Enter verification code: ';
// Check Param on redirected URL, for ?code=#############
// you have to copy only ?code= $_GET parms data and paste in console
$authCode = trim(fgets(STDIN)); // Get code after Authentication
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);
// Check to see if there was an error.
if (array_key_exists('error', $accessToken)) {
throw new Exception(join(', ', $accessToken));
}
}
// Save the token to a file.
if (!file_exists(dirname($tokenPath))) {
mkdir(dirname($tokenPath), 0700, true);
}
file_put_contents($tokenPath, json_encode($client->getAccessToken()));
}
return $client;
}
$client = getClient();
$accessToken = $client->getAccessToken();
$service = new Google_Service_Calendar($client);
$summary = $_POST['summary'];
$description = $_POST['description'];
$fecha_inicio = $_POST['fecha_inicio'];
$hora_inicio = $_POST['hora_inicio'];
$fecha_fin = $_POST['fecha_fin'];
$h_inicio = explode(" ",$hora_inicio);
$hora = $h_inicio[0].":00";
$minutos = "00:30:00";
$secs = strtotime($minutos)-strtotime("00:00:00");
$result = date("H:i:s",strtotime($hora)+$secs);
$inicio = $fecha_inicio."T".$hora_inicio;
$fin = $fecha_fin."T".$result;
$attendee = $_POST['attendee'];
$_info = array(
'summary' => $summary,
'location' => 'address',
'description' => $description,
'start' => array(
'dateTime' => $inicio,
'timeZone' => 'America/Bogota',
),
'end' => array(
'dateTime' => $fin,
'timeZone' => 'America/Bogota',
),
'attendees' => array(
array('email' => $attendee)
),
'reminders' => array(
'useDefault' => FALSE,
'overrides' => array(
array('method' => 'email', 'minutes' => 24 * 60),
array('method' => 'popup', 'minutes' => 10),
),
),
);
//die(var_dump($_info));
$event = new Google_Service_Calendar_Event($_info);
$opts = array('sendNotifications' => true, 'conferenceDataVersion' => true); // send Notification immediately by Mail or Stop Hangout Call Link
$event = $service->events->insert( 'primary', $event, $opts );
die(var_dump($event));
printf('Event created: %s\n', $event->htmlLink);
?>
When your code runs the first time the user will be asked to consent to your application accessing their data. If the user consents then your code is storing the access token and refresh token in a file called token.json.
The next time the code runs it will check isAccessTokenExpired if it is it will use the refresh token stored in token.json to request a new access token and grant you access to the users data.
The way your code is written this will only be working for a single user as you are only storing to a single token.json file.
I am a bit confused on the Access token. I have written a PHP script which inserts the data I get from the POST request , I already have authorized the App and it does add the Row at the end of the Sheet.
My Question is how I refresh the token when it get implemented on the server, as it will add the POST data.
Here is the Code
<?php
// Load the Google API PHP Client Library.
require_once __DIR__ . '/vendor/autoload.php';
session_start();
$client = new Google_Client();
$client->setAuthConfigFile(__DIR__ . '/client_secrets.json');
$client->addScope(Google_Service_Sheets::SPREADSHEETS);
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
$client->setAccessToken($_SESSION['access_token']);
$lead = array (
"first_name" => $_POST['name'],
"email" => $_POST['email']
);
$sid = "sheet id on which the row is added";
addRowToSpreadsheet($lead, $client , $sid);
} else {
$redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/fb/oauth2callback.php';
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
function addRowToSpreadsheet($ary_values = array(), $client , $sid) {
$sheet_service = new Google_Service_Sheets($client);
$fileId = $sid;
$values = array();
foreach( $ary_values AS $d ) {
$cellData = new Google_Service_Sheets_CellData();
$value = new Google_Service_Sheets_ExtendedValue();
$value->setStringValue($d);
$cellData->setUserEnteredValue($value);
$values[] = $cellData;
}
// Build the RowData
$rowData = new Google_Service_Sheets_RowData();
$rowData->setValues($values);
// Prepare the request
$append_request = new Google_Service_Sheets_AppendCellsRequest();
$append_request->setSheetId(0);
$append_request->setRows($rowData);
$append_request->setFields('userEnteredValue');
// Set the request
$request = new Google_Service_Sheets_Request();
$request->setAppendCells($append_request);
// Add the request to the requests array
$requests = array();
$requests[] = $request;
// Prepare the update
$batchUpdateRequest = new Google_Service_Sheets_BatchUpdateSpreadsheetRequest(array(
'requests' => $requests
));
try {
// Execute the request
$response = $sheet_service->spreadsheets->batchUpdate($fileId, $batchUpdateRequest);
if( $response->valid() ) {
// Success, the row has been added
return true;
}
} catch (Exception $e) {
// Something went wrong
error_log($e->getMessage());
}
return false;
}
?>
I have tried hosting the app on the server and it doesn't add a new row in the Sheet, I think this is a problem due to the Access Token
Please Help
There is actually a PHP Quickstart for Sheets API which includes how to refresh tokens. Here's a snippet:
// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}
There's also a Refreshing an access token (offline access) guide with regard to refresh tokens
Access tokens periodically expire. You can refresh an access token
without prompting the user for permission (including when the user is
not present) if you requested offline access to the scopes associated
with the token.
If you use a Google API Client Library, the client object refreshes
the access token as needed as long as you configure that object for
offline access. If you are not using a client library, you need to set
the access_type HTTP query parameter to offline when redirecting the
user to Google's OAuth 2.0 server. In that case, Google's
authorization server returns a refresh token when you exchange an
authorization code for an access token. Then, if the access token
expires (or at any other time), you can use a refresh token to obtain
a new access token.
If your application needs offline access to a Google API, set the API
client's access type to offline:
$client->setAccessType("offline");
I'm using Google Calendar API to display events on fullcalendar(so using a json object in my view). I'm using codeignitor php framework, and I have a few functions in my controller to create a new client then I use that in the oauth2callback() function to exchange my code for an access_token then I start calling the service in gcalendar() and gcalendar_events. I have set the accessType to offline but that doesn't seem to make me access the events offline. It works great except that I'm redirected to log in again every time the session ends. I don't want that, I want them to display all the time after the session ends. I am trying to use a refresh token in case the access_token expires to see if that would fix the problem.
this is the code in my controller
function getClient() {
$client = new Google_Client();
$client->setApplicationName("DL Calendar");
$client->setAuthConfig('application/client_secrets.json');
$client->addScope('profile');
$client->setIncludeGrantedScopes(true);
$client->setAccessType('offline');
return $client;
}
function gcalendar() {
$this->load->add_package_path(APPPATH . 'vendor/autoload');
$client = $this->getClient();
//$client->setRedirectUri(site_url('calendar/index'));
$client->addScope(Google_Service_Calendar::CALENDAR);
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
$client->setAccessToken($_SESSION['access_token']);
$access_token = $_SESSION['access_token'];
$service = new ]Google_Service_Calendar($client);
$calendar = new Google_Service_Calendar_Calendar();
//$calendarList = $service->calendarList->listCalendarList();
$calendar = $service->calendars->get('primary');
$params = array(
'owner_id' => get_current_user_id(),
'title' => get_current_user(). ' ' .'Google Calendar',
'type' => 'gcal',
'url' => $calendar->id,
);
$calendar_id = $this->Calendar_model->add_calendar($params);
redirect('calendar/index');
} else {
$redirect_uri = site_url('calendar/oauth2callback');
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
$this->session->set_flashdata('success', 'Event Successfully Added');
}
function oauth2callback() {
//Build the client object
$client = $this->getClient();
$client->addScope(Google_Service_Calendar::CALENDAR);
$service = new Google_Service_Calendar($client);
$url = parse_url($_SERVER['REQUEST_URI']); parse_str($url['query'], $params);
$code = $params['code'];
//To exchange an authorization code for an access token, use the authenticate method:
if (! isset($code)) {
$auth_url = $client->createAuthUrl();
header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
} else {
$token = $client->fetchAccessTokenWithAuthCode($code);
$client->setAccessToken($token);
$client->authenticate($code);
$_SESSION['access_token'] = $client->getAccessToken();
$redirect_uri = site_url('calendar/gcalendar');
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
}
function gcalendar_events() {
$client = $this->getClient();
$client->addScope(Google_Service_Calendar::CALENDAR);
// $client->setRedirectUri(site_url('calendar/gcalendar'));
$client->setAccessType('offline'); //need calendar events to appear even if not logged in to google
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
$client->setAccessToken($_SESSION['access_token']);
$access_token = $_SESSION['access_token'];
$service = new Google_Service_Calendar($client);
$id = 'primary';
$calendar = new Google_Service_Calendar_Calendar();
$calendar = $service->calendars->get('primary');
$event = new Google_Service_Calendar_Event();
$events = $service->events->listEvents($id);
foreach ($events->getItems() as $event) {
$startTime = strtotime($event->getStart()->dateTime) ;
$endTime = strtotime($event->getEnd()->dateTime);
$start = date('Y-m-d H:i:s', $startTime);
$end = date('Y-m-d H:i:s', $endTime);
$eventsArr[] = array(
'title' => $event->getSummary(),
'start'=> $start,
'end' => $end,
);
}
// Return a single `events` with all the `$eventsArr`
echo json_encode($eventsArr);
}
}
Is the problem in my session ending? or does the access token expire and I need a refresh token? where do I set the refresh token cause I tried putting it in more that one place and I get an error message that refresh token has to be set as part off setAccessToken. I put it all over and still got error messages.
Here is the code I used
if ($client->isAccessTokenExpired()) {
$refresh_token = $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
$client->setAccessToken($refresh_token);
$_SESSION['access_token'] = $refresh_token;
$this->load->helper('file');
write_file('application/client_secrets.json', json_encode($client->getAccessToken()));
} else {
$access_token = $_SESSION['access_token'];
}
I just noticed that it once used to say 'grant offline access' in my authorization but now it no longer mentions that while google documentation says
After a user grants offline access to the requested scopes, you can
continue to use the API client to access Google APIs on the user's
behalf when the user is offline. The client object will refresh the
access token as needed.
I think this Offline access in OAuth guide might help you. Since you're using PHP, try setting the access type to "offline".
If your application needs offline access to a Google API, set the API client's access type to offline:
$client->setAccessType("offline");
After a user grants offline access to the requested scopes, you can continue to use the API client to access Google APIs on the user's behalf when the user is offline. The client object will refresh the access token as needed.
I'm trying to setup a Push Notifications for Google Calendar using PHP and V3 api.
I've got the Auth2.0 Permission and I'm able to create events on google from my application. Now I want to know when a user makes any change on google calendar (CRUD Operations).
This is my code:
private $imageService;
public $google_client;
public $google_calendar;
public function __construct()
{
$this->imageService = new ImageService();
$this->google_client = new Google_Client();
$this->google_client->setApplicationName($_ENV['GOOGLE_APP_NAME']);
$this->google_client->setDeveloperKey($_ENV['GOOGLE_API_KEY']);
$this->google_client->setClientId($_ENV['CLIENT_ID']);
$this->google_client->setClientSecret($_ENV['CLIENT_SECRET']);
$this->google_client->setAccessType('offline');
$this->google_client->setIncludeGrantedScopes(true);
$this->google_client->setScopes(array('email', 'profile', 'https://www.googleapis.com/auth/plus.me', 'https://www.googleapis.com/auth/calendar'));
$this->google_calendar = new Google_Service_Calendar($this->google_client);
}
public function googleCalendarWatch($uuid){
$channel = new Google_Service_Calendar_Channel($this->google_client);
$channel->setId($uuid);
$channel->setType('web_hook');
$channel->setAddress("https://example.com/google/googleNotifications");
$channel->setExpiration("1919995862000");
$this->google_calendar->events->watch('primary', $channel);
}
And this is the output:
Google_Service_Calendar_Channel Object (
[internal_gapi_mappings:protected] => Array ( )
[address] =>
[expiration] => 1426272395000
[id] => aee2b430-34bf-42bc-a597-ada46db42799
[kind] => api#channel
[params] =>
[payload] =>
[resourceId] => 51IKGpOwCJ6EMraQMUc1_04MODk
[resourceUri] => https://www.googleapis.com/calendar/v3/calendars/primary/events?key=AIzaSyBFUvq3OZO6ugAKvz7l8NgLS0V6DUJq8Vc&alt=json
[token] =>
[type] =>
[modelData:protected] => Array ( )
[processed:protected] => Array ( ) )
This far I don't know why address returns null, maybe that's the problem. But I don't know how to fix it.
Also reading this: #26730263 and looking my own code there's no much difference.
I did all the stuff that google says, Register domain, add credentials, api key, push domain allowed and all..
Create a notification channel for every individual resource, then any modification to that resource you will be then notified. Information below is directly from Google (Creating notification channels).
Making watch requests:
Each watchable Google Calendar API resource has an associated watch method at a URI of the following form:
https://www.googleapis.com/**apiName**/**apiVersion**/**resourcePath**/watch
To set up a notification channel for messages about changes to a particular resource, send a POST request to the watch method for the resource.
Each notification channel is associated both with a particular user and a particular resource (or set of resources). A watch request will not be successful unless the current user owns or has permission to access this resource.
Example:
Start watching for changes to a collection of events on a given calendar:
POST https://www.googleapis.com/calendar/v3/calendars/my_calendar#gmail.com/events/watch
Authorization: Bearer auth_token_for_current_user
Content-Type: application/json
{
"id": "01234567-89ab-cdef-0123456789ab", // Your channel ID.
"type": "web_hook",
"address": "https://example.com/notifications", // Your receiving URL.
...
"token": "target=myApp-myCalendarChannelDest", // (Optional) Your channel token.
"expiration": 1426325213000 // (Optional) Your requested channel expiration time.
}
}
Reference: Google (Creating notification channels) (19-Mar-2018).
Address?
Token?
<?php
require __DIR__ . '/vendor/autoload.php';
if (php_sapi_name() != 'cli') {
throw new Exception('This application must be run on the command line.');
}
/**
* Returns an authorized API client.
* #return Google_Client the authorized client object
*/
function getClient()
{
$client = new Google_Client();
$client->setApplicationName('Google Calendar API PHP Quickstart');
$client->setScopes(Google_Service_Calendar::CALENDAR_READONLY);
$client->setAuthConfig('credentials.json');
$client->setAccessType('offline');
$client->setPrompt('select_account consent');
// Load previously authorized token from a file, if it exists.
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
$tokenPath = 'token.json';
if (file_exists($tokenPath)) {
$accessToken = json_decode(file_get_contents($tokenPath), true);
$client->setAccessToken($accessToken);
}
// If there is no previous token or it's expired.
if ($client->isAccessTokenExpired()) {
// Refresh the token if possible, else fetch a new one.
if ($client->getRefreshToken()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
} else {
// Request authorization from the user.
$authUrl = $client->createAuthUrl();
printf("Open the following link in your browser:\n%s\n", $authUrl);
print 'Enter verification code: ';
$authCode = trim(fgets(STDIN));
// Exchange authorization code for an access token.
$accessToken = $client->fetchAccessTokenWithAuthCode($authCode);
$client->setAccessToken($accessToken);
// Check to see if there was an error.
if (array_key_exists('error', $accessToken)) {
throw new Exception(join(', ', $accessToken));
}
}
// Save the token to a file.
if (!file_exists(dirname($tokenPath))) {
mkdir(dirname($tokenPath), 0700, true);
}
file_put_contents($tokenPath, json_encode($client->getAccessToken()));
}
return $client;
}
// Get the API client and construct the service object.
$client = getClient();
$service = new Google_Service_Calendar($client);
// Print the next 10 events on the user's calendar.
$calendarId = 'primary';
$optParams = array(
'maxResults' => 10,
'orderBy' => 'startTime',
'singleEvents' => true,
'timeMin' => date('c'),
);
$results = $service->events->listEvents($calendarId, $optParams);
$events = $results->getItems();
if (empty($events)) {
print "No upcoming events found.\n";
} else {
print "Upcoming events:\n";
foreach ($events as $event) {
$start = $event->start->dateTime;
if (empty($start)) {
$start = $event->start->date;
}
printf("%s (%s)\n", $event->getSummary(), $start);
}
}
Full Tutorial