Asked  7 Months ago    Answers:  5   Viewed   41 times

I am writing a PHP application that's supposed to allow users to add certain events to a private Google Calendar. The calendar is owned by me, and I need a way for PHP to communicate with the calendar API using fixed credentials (everyone can add events using a form on the website, but the calendar itself is not publicly visible).

From what I have read, this is possible using ClientLogin in the v1 API. In the v3 API, however, the available options are OAuth2.0 or the API key. Using the API key doesn't seem to work, since it can only be used for requests that don't require authorization, and OAuth doesn't seem right either, because users are not supposed to access their own calendars, but the one my application uses.

I thought about getting the OAuth token programatically, but that's bound to break sooner or later, since the OAuth dialog can use captchas.

This seems to be such a standard use case — a web application that lets users interact with a single calendar in some predefined ways — yet I can't find any documentation on how to make it happen in the v3 API. Can anyone help me?

 Answers

74

You will need to use both the Developer Key (API Key) and OAuth2. The developer key authenticates who wrote the software and is used for things like quota which is on a per developer basis not a per user basis. OAuth2 is for user authentication and will be need to access the non-public calendar.

OAuth2 has a renew token from which you can generate a session token and this means that you will not need to screen scrape the OAuth screens to get authenticated. To get this I would write a little command line application, or you use a one off PHP page.

  1. Under the Google Api Console go to API Access
  2. Generate a new Client ID and choose Installed Application ( as you will be authenticating you server as you not as your user)
  3. Either using a console app or a one off PHP page authenticate using OAuth and your google account (the one with the calendar you want access to)
  4. In the return from the authentication there should be a renew token, (called renew or refresh or something similar). Save this string and make it available to your PHP site.
  5. When you need to access the service your OAuth library should have a renew/refresh call. There is an example using .Net below.

private IAuthorizationState CreateAuthorization(NativeApplicationClient arg)
 {
   // Get the auth URL:
   IAuthorizationState state = new AuthorizationState(new[] { AdsenseService.Scopes.AdsenseReadonly.GetStringValue() });
   state.Callback = new Uri(NativeApplicationClient.OutOfBandCallbackUrl);
   if (refreshToken.IsNotNullOrEmpty()) // refreshToken you stored in step 4
   {
     try
     {
       state.RefreshToken = refreshToken;
       if (arg.RefreshToken(state))     // This is calling out to the OAuth servers with the refresh token getting back a session token, returns true if successful.
       {
         if (state.RefreshToken != refreshToken) // if the refresh token has changed, save it.
         {
           PersistRefreshToken(authorization.RefreshToken);
         }
         return this.authorization = state; // Retain the authorization state, this is what will authenticate your calls.
       }
     }
     catch (ProtocolException ex) {...}

The AuthorisationState that has now been renewed can then be used to authenticate call you make to the API. this state can be used many time until it expires and then can be refreshed. As you are authenticating your application as yourself not as a user this AuthorisationState can be shared by all you sessions. Both the current AuthorisationState and the refresh token should be kept securely on your server and never sent to the client, if you ever sent these as part of a response your clients would have the same privileges as your code application

Wednesday, March 31, 2021
 
axiomer
answered 7 Months ago
44

It seems the mistake is because you send a DELETE http verb instead of a POST. Also, the XML namespace attributes in the entry are not required.

By the way, maybe this is a typo, you wrote 'content-lenght' instead of content-length.

Don't send any content in the entry for deletions, as it's not required (and not documented).

$xmlBuild = "<feed xmlns='http://www.w3.org/2005/Atom'
    xmlns:batch='http://schemas.google.com/gdata/batch'
    xmlns:gd='http://schemas.google.com/g/2005'
    xmlns:gContact='http://schemas.google.com/contact/2008'>";
$xmlBuild .= "<entry>";
$xmlBuild .= "<batch:id>1</batch:id><batch:operation type='delete'/>";
$xmlBuild .= "<id>http://www.google.com/m8/feeds/contacts/my.domain/base/1b93ef80b806243</id>";
$xmlBuild .= "</entry>";
$xmlBuild .= "</feed>";

$len = strlen($xmlBuild);
$options = array(
    "headers" => array(
        "Content-type" => "application/atom+xml; charset=UTF-8;",
        "Content-length" => $len
    ),
    "body" => $xmlBuild
);

$httpClient = $client->authorize();
$request = $httpClient->post("https://www.google.com/m8/feeds/contacts/my.domain/full/batch", $options); 
// or $request = $httpClient->sendRequest('POST', "https://www.google.com/m8/feeds/contacts/my.domain/full/batch", $options); 

$response = $request->getBody()->getContents();

print_r($response);
Saturday, May 29, 2021
 
Domiik
answered 5 Months ago
97

First check the settings in the developer console of Google to see if your RedirectUri is the same and that the API is activated (although if you already got that .json, then I assume it is.

You have to go through the Google Auth Prompt Screen at least 1 time to get a refresh token in your .json, and if your RedirectUri is taking you nowhere, you won't be able to get your refresh token or even the access validated.

You can also try a service account if you're doing small file transactions and don't need a user validation for the process of your script. Good Luck.

Saturday, May 29, 2021
 
nhunston
answered 5 Months ago
38

I don't know how to do this with the Python Client or the Calendar API (I'm just using a ruby OAuth2 library for access to the Contacts API), but I found I needed to request "offline" access from the user.

This is done by adding the an "access_type" parameter with the value "offline" to the authorization url (the one you redirect the user to to click "I want to allow this application to get at my data").

If you do this, you get a refresh_token back in your JSON response when you ask google for access tokens. Otherwise you only get the access token (which is valid for an hour).

This requirement was apparently added recently (and may not be wonderfully documented).

You used to get a refresh token without having to get specific "offline" permission.

Hope this points you in the right direction.

Tuesday, August 10, 2021
 
ritch
answered 3 Months ago
31

The docs are really sketchy on this but it seems you have to increment the sequence property each time you do an update. It's a revision control mechanism.

http://www.kanzaki.com/docs/ical/sequence.html

Wednesday, October 13, 2021
 
Conan
answered 2 Weeks ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 
Share