0

Recently I've decided to give users at my site the following functionality: online time slot booking in calendars of my employees. All employees have Google accounts linked to our domain in Google Apps (free edition). Users make booking in some front-end (e.g. not directly in employees Google calendars), then request is processed at our PHP server and if it is correct, server should be able to create new calendar entry in selected employee Google calendar. Note - neither user, nor employee should not be asked for authentication during booking.

I've scanned Google calendar v3 API and forums and still didn't get neither clear answer not concise examples - is such scenario possible with Google calendar? Can someone help me to answer the Q (and if possible - share a link with proper example)?

valieand
  • 11
  • 5

1 Answers1

0

Are you familiar with adding an event with authentication? Like i answered here.
And like in the code below. If you are... you can go to the next level ;)

(You can download the Google Api Php-client on this page. On this page there is a tutorial. It's for Google+ but the principle is the same)

The first time you would always need authentication. There is no way around that. In the example below you get a token back after authentication, which includes an access_token and a refresh_token. The access_token is only valid for 3600 seconds and is used for direct access. When the access_token is expired you get a 401 error and you can use the refresh_token (together with client_id, client_secret and the correct grant_type) to request a new access_token.

You can find some more information on this page (at the bottom "Using a Refresh Token"). There are some limits in numbers to request these, mentioned on that page at the bottom.


This is some testing code i made the last time i helped someone with this. It only shows getting the authentication and storing the token in a cookie. It does not request a new access_token after it expires.

But as i already said you need to store the token (in a database?) and when you get a 401 you need to request a new access_token with the client_id, client_secret, correct grant_type and refresh_token. There is no (re-)authentication necessary for that.

<?php
error_reporting(E_ALL);
require_once 'google-api-php-client/src/Google_Client.php';
require_once 'google-api-php-client/src/contrib/Google_CalendarService.php';
session_start();

if ((isset($_SESSION)) && (!empty($_SESSION))) {
   echo "There are cookies<br>";
   echo "<pre>";
   print_r($_SESSION);
   echo "</pre>";
}

$client = new Google_Client();
$client->setApplicationName("Google Calendar PHP Starter Application");
$client->setClientId('###');
$client->setClientSecret('###');
$client->setRedirectUri('http://###/add_calendar.php'); // <- registered web-page
//$client->setDeveloperKey('###'); // <- not always needed
$cal = new Google_CalendarService($client);

if (isset($_GET['logout'])) {
  echo "<br><br><font size=+2>Logging out</font>";
  unset($_SESSION['token']);
}

if (isset($_GET['code'])) {
  echo "<br>I got a code from Google = ".$_GET['code']; // You won't see this if redirected later
  $client->authenticate(); // $_GET['code']
  $_SESSION['token'] = $client->getAccessToken();
  header('Location: http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF']);
  // not strictly necessary to redirect but cleaner for the url in address-bar
  echo "<br>I got the token = ".$_SESSION['token']; // <-- not needed to get here unless location uncommented
}

if (isset($_SESSION['token'])) {
  echo "<br>Getting access";
  $client->setAccessToken($_SESSION['token']);
}

if ($client->getAccessToken()){
  echo "<hr><font size=+1>I have access to your calendar</font>";
  $event = new Google_Event();
  $event->setSummary('=== I ADDED THIS ===');
  $event->setLocation('The Neighbourhood');
  $start = new Google_EventDateTime();
  $start->setDateTime('2013-11-29T10:00:00.000-05:00');
  $event->setStart($start);
  $end = new Google_EventDateTime();
  $end->setDateTime('2013-11-29T10:25:00.000-05:00');
  $event->setEnd($end);
  $createdEvent = $cal->events->insert('###', $event); // <- ### = email of calendar
  echo "<br><font size=+1>Event created</font>";

  echo "<hr><br><font size=+1>Already connected</font> (No need to login)";

} else {

  $authUrl = $client->createAuthUrl();
  print "<hr><br><font size=+2><a href='$authUrl'>Connect Me!</a></font>";

}

$url = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
echo "<br><br><font size=+2><a href=$url?logout>Logout</a></font>";

?>

Edit:

If you don't want the initial authentication Google has its "Service Acounts", as you pointed out.
(I didn't know about these :)

I dug up some links where you can find code to use after you got the key-file.

Edit Google calendar events from Google service account: 403
Access Google calendar events from with service account: { "error" : "access_denied" }. No google apps
Google OAuth 2.0 Service Account - Calendar API (PHP Client)
https://groups.google.com/forum/#!topic/google-api-php-client/B7KXVQvx1k8
https://groups.google.com/forum/?fromgroups#!topic/google-api-php-client/IiwRBKZMZxw


Edit #2: YES, got it working. (even with my free Google account ;)

Here is the working code:

<?
error_reporting(E_ALL);
require_once 'google-api-php-client/src/Google_Client.php';
require_once 'google-api-php-client/src/contrib/Google_CalendarService.php';
session_start();

echo "Busy<br>";

const CLIENT_ID = 'xxxxxxxxxx.apps.googleusercontent.com';
const SERVICE_ACCOUNT_NAME = 'xxxxxxxxxxx@developer.gserviceaccount.com';
const KEY_FILE = 'xxxxxxxxxxxxxxxxxxx-privatekey.p12';
const CALENDAR_NAME = 'xxxxxxx@gmail.com';

$client = new Google_Client();
$client->setApplicationName("mycal");

if (isset($_SESSION['token'])) {
 $client->setAccessToken($_SESSION['token']);
}

$key = file_get_contents(KEY_FILE);
$client->setClientId(CLIENT_ID);
$client->setAssertionCredentials(new Google_AssertionCredentials(
  SERVICE_ACCOUNT_NAME,
  array('https://www.google.com/calendar/feeds/'),
  $key)
);

$client->setClientId(CLIENT_ID);
$service = new Google_CalendarService($client);

$event = new Google_Event();
$event->setSummary('=== I MADE THIS ===');
$event->setLocation('Somewhere else');
$start = new Google_EventDateTime();
$start->setDateTime('2013-11-30T10:00:00.000-02:00');
$event->setStart($start);
$end = new Google_EventDateTime();
$end->setDateTime('2013-11-30T10:25:00.000-02:00');
$event->setEnd($end);

$createdEvent = $service->events->insert(CALENDAR_NAME, $event);

echo "Done<br>";

?>

I needed to make a "new project" on https://cloud.google.com/console#/project. I couldn't use the existing one. After activating "Calendar API" and registering a new Web-app with Certificate i only needed to share my calendar with the xxxx@developer.gserviceaccount.com and above code worked
(without authentication).

Edit #3: and now it also seems to be working in the default "Google+ API Project"

Community
  • 1
  • 1
Rik
  • 1,982
  • 1
  • 17
  • 30
  • Thanks, but it doesn't address a question, because using refresh_token (1) employee should allow access at least first time (2) there are limits in number of requests with refresh_token. What I need is permanent access w/o limits and w/o need to ask employee to allow access. And I guess it should be possible under Google Apps for Business. However I can't find any info how to do that. – valieand Nov 27 '13 at 10:54
  • I'm not familiar with the "Apps for Business" but if the employee allows access the first time, you should **keep** using **the same** refresh_token **without a time limit** (or until the employee revokes access for the token). The limit is for **number** of refresh_token: `one limit per client/user combination, and another per user across all clients`. It's not a limit for how many times you can request a access_token with it. So you **keep** the refresh_token and don't ask for new refresh_tokens. (If you **do** keep asking for refresh_tokens **then** old(er) ones can become invalid) (1/2) – Rik Nov 27 '13 at 11:26
  • (2/2) I think this is how most apps (e.g. on Android) work with authentication for Google services. One time authentication via the Web and they also don't expire because they use the refresh_token they got, to request access_tokens. – Rik Nov 27 '13 at 11:29
  • Thanks, I also think this is how most apps work. However, I think that for Google Apps there should be better way to do the same, e.g. w/o user consent. This should be possible either through "service accounts" or API keys. Here is some info on that https://developers.google.com/console/help/new/ – valieand Nov 28 '13 at 11:50
  • I see. Didn't know about the "Service Acounts". I added some links to code to my answer at the bottom. Do you know if i should be able to get a key-file with a normal (free) Google account? Or do you need a Business account? (If i can find the option i could do some test and post working code myself) – Rik Nov 28 '13 at 13:07
  • YES... I got it working (with my free Google-account). Edit #3 in my answer has the working code. (without authentication ;) – Rik Nov 28 '13 at 14:53
  • Super. Lucky you )) I'm trying to reproduce but get "(401) Invalid Credentials". – valieand Nov 28 '13 at 20:11
  • If I slightly change your code to: $client->setAssertionCredentials(new Google_AssertionCredentials(SERVICE_ACCOUNT_NAME, array('https://www.google.com/calendar/feeds/'), $key, 'notasecret', 'http://oauth.net/grant_type/jwt/1.0/bearer', CALENDAR_NAME)); - it returns "Error refreshing the OAuth2 token, message: '{ "error" : "access_denied" }'" – valieand Nov 28 '13 at 20:15
  • Are you using the "Google Business Apps"? In that case i think you would need to enable this feature in your Google Apps control panel (cPanel). I've never seen the CALENDAR_NAME in that call. See [here](https://groups.google.com/forum/#!topic/google-api-php-client/B7KXVQvx1k8). Also see [here](http://stackoverflow.com/questions/13111396/access-google-calendar-events-from-with-service-account-error-access-den). I think the answer on that last one should work. – Rik Nov 28 '13 at 21:09
  • And did you share the Google calendar with the SERVICE_ACCOUNT_NAME XXXXXX@developer.gserviceaccount.com? – Rik Nov 28 '13 at 21:11
  • For normal accounts the event will be created by the XXXXXX@developer.gserviceaccount.com. This **is** visible in the event. If that's not ok you'll need to use a "prn". See [here](http://stackoverflow.com/questions/18457241/google-oauth-2-0-service-account-calendar-api-php-client). And that will only work with a hosted domain. – Rik Nov 28 '13 at 21:16
  • YES... I got it working too... )) Problem was in old configuration of config.php. Thanks!!! – valieand Nov 28 '13 at 21:46