I'm having a problem implementing Google Admin SDK in PHP. It always give error
Fatal error: Uncaught exception 'Google_Service_Exception' with message 'Error calling POST
https://www.googleapis.com/admin/directory/v1/users: (403) Not Authorized to access this
resource/api' in ..\google-api-php-client\src\Google\Http\REST.php on line 79
Here is my code :
<?php
session_start();
/* * **********************************************
Make an API request authenticated with a service
account.
* ********************************************** */
set_include_path("google-api-php-client/src" . PATH_SEPARATOR . get_include_path());
require_once 'Google/Client.php';
require_once 'Google/Service/Directory.php';
/* * **********************************************
ATTENTION: Fill in these values! You can get
them by creating a new Service Account in the
API console. Be sure to store the key file
somewhere you can get to it - though in real
operations you'd want to make sure it wasn't
accessible from the webserver!
The name is the email address value provided
as part of the service account (not your
address!)
Make sure the Books API is enabled on this
account as well, or the call will fail.
* ********************************************** */
$client_id = 'my_client_id'; //Client ID
$service_account_name = 'email'; //Email Address
$key_file_location = 'key.p12'; //key.p12
if ($client_id == '<YOUR_CLIENT_ID>' || !strlen($service_account_name) || !strlen($key_file_location)) {
echo missingServiceAccountDetailsWarning();
}
$client = new Google_Client();
$client->setApplicationName("appname");
$service = new Google_Service_Directory($client);
/* * **********************************************
If we have an access token, we can carry on.
Otherwise, we'll get one with the help of an
assertion credential. In other examples the list
of scopes was managed by the Client, but here
we have to list them manually. We also supply
the service account
* ********************************************** */
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://www.googleapis.com/auth/admin.directory.user'), $key
);
$client->setAssertionCredentials($cred);
if ($client->getAuth()->isAccessTokenExpired()) {
$client->getAuth()->refreshTokenWithAssertion($cred);
}
$_SESSION['service_token'] = $client->getAccessToken();
/* * **********************************************
We're just going to make the same call as in the
simple query as an example.
* ********************************************** */
$familyName = filter_input(INPUT_GET, 'familyname');
$givenName = filter_input(INPUT_GET, 'givenname');
$password = filter_input(INPUT_GET, 'password');
$primaryEmail = filter_input(INPUT_GET, 'email');
$user = new Google_Service_Directory_User();
$name = new Google_Service_Directory_UserName();
$name->setFamilyName($familyName);
$name->setGivenName($givenName);
$user->setName($name);
$user->setHashFunction("MD5");
$user->setPrimaryEmail($primaryEmail);
$user->setPassword(hash("md5", $password));
$result = $service->users->insert($user);
There is no documentation and I'm very confused with this error.
I had a similar problem until I set the "sub" account on the credentials object which is the email address for the administrative user that you want to perform the API action as.
So in Google Apps Admin for your domain I assume you've created a user and added them to and admin role that has API access to users right? In your code above you just need to add (with correct email address of course):
$cred->sub = "adminuser#domain.com";
Add that right before:
$client->setAssertionCredentials($cred);
I did a full example for someone else that you might be able to reference too: https://gist.github.com/fillup/9fbf5ff35b337b27762a
I had a similar problem but found a solution to this by going through this blog
http://michaelseiler.net/2014/12/16/google-admin-sdk-api-with-php/
Assuming you have done all the necessary setting up on the Admin Console (i.e. delegating domain authority), then pay particular attention to the line
`$cred = new Google_Auth_AssertionCredentials($service_account_name, array('https://www.googleapis.com/auth/admin.directory.user'), $key);`
this should be:
`$user_to_impersonate = 'adminaccount#example.com';$scopes =array('https://www.googleapis.com/auth/admin.directory.user');$cred = new Google_Auth_AssertionCredentials(
$service_account_name,
$scopes,
$key,
'notasecret', // Default P12 password
'http://oauth.net/grant_type/jwt/1.0/bearer', // Default grant type
$user_to_impersonate);`
I changed that and boom....
I think i was receiving this error when i tried to query a user.
During the creation of the JWT_CLAIM portion of the assertion, you must specify a domain admin account that your service account can impersonate... this part was poorly documented, i thought.
i dont know exactly how the google php library does it, but i didnt see anywhere in your code where you specify that account email address. if you have looked at the oauth2 documentation, in the jwt_claim secion, there is an "additional claims" subsection that says:
To obtain an access token that grants an application delegated access to a resource, include the email address of the user in the JWT claim set as the value of the sub field.
I had to include that admin email account in the creation of my assertion before i could even query the directory.users API.
i apologize if this isnt helpful, but im not using the library for php, im building my own (with php).
Related
i am struggling with google API. I am creating a website where I would like to read the groups of which the authenticated user is a member.
I have read everything about the subject, but many examples are very old and in the meantime things have changed.
This is the code I'm testing:
<?php
require 'lib/google-api/vendor/autoload.php';
// Creating new google client instance
$client = new Google_Client();
// Enter your Client ID
$client->setClientId('***.apps.googleusercontent.com');
// Enter your Client Secrect
$client->setClientSecret('***');
// Enter the Redirect URL
$client->setRedirectUri('***');
$client->setApplicationName("Test");
// Adding those scopes which we want to get (email & profile Information)
$client->addScope("email");
$client->addScope("profile");
$client->addScope("https://www.googleapis.com/auth/admin.directory.group.readonly");
$client->addScope("https://www.googleapis.com/auth/admin.directory.group.member.readonly");
if(isset($_GET['code'])){
$token = $client->fetchAccessTokenWithAuthCode($_GET['code']);
if(!isset($token["error"])){
$client->setAccessToken($token['access_token']);
// getting profile information
$google_oauth = new Google_Service_Oauth2($client);
$google_account_info = $google_oauth->userinfo->get();
// Storing data into database
$email = $google_account_info->email;
$full_name = $google_account_info->name;
echo $email . "<br>";
// this is needed only if you need to perform
// domain-wide admin actions, and this must be
// an admin account on the domain; it is not
// necessary in your example but provided for others
$client->setSubject('***');
// set the authorization configuration using the 2.0 style
$client->setAuthConfig("***.json");
$adminService = new Google_Service_Directory($client);
$googleGroups = $adminService->groups->listGroups(array('domain'=>'mydomain.com'));
$groups = $googleGroups->getGroups();
}
}
?>
I am getting the following error
Uncaught Google_Service_Exception: {
"error": "unauthorized_client",
"error_description": "Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested."
}
I have already done a Google Workspace Domain-Wide Delegation of Authority (https://developers.google.com/admin-sdk/directory/v1/guides/delegation) and I created a user to impersonate with read permissions on groups and users.
Where am I doing wrong? Thank you!
After some testing I made it work...
First of all i needed to give rights to the service account, not just at the one to impersonate.
$client = new Google_Client();
// Client ID
$client->setClientId(***);
// Client Secrect
$client->setClientSecret(***);
// Redirect URL
$client->setRedirectUri('https://www.mywebsite.com');
$client->setApplicationName('App name');
// Scopes for group reading, profile and email info
$client->addScope('https://www.googleapis.com/auth/admin.directory.group.readonly');
$client->addScope('https://www.googleapis.com/auth/userinfo.email');
$client->addScope('https://www.googleapis.com/auth/userinfo.profile');
if(isset($_GET['code'])){
$token = $client->fetchAccessTokenWithAuthCode($_GET['code']);
if(!isset($token["error"])){
$client->setAccessToken($token['access_token']);
$google_oauth = new Google_Service_Oauth2($client);
// User info
$google_account_info = $google_oauth->userinfo->get();
$email = $google_account_info->email;
// Service account info
$client->setAuthConfig('***.json');
// Who i want to impersonate
$client->setSubject('admin#website.com');
$service = new Google_Service_Directory($client);
// Search groups of the logged user
$optParams = array(
'domain' => 'mywebsite.com',
'userKey' => $email
);
$googleGroups = $service->groups->listGroups($optParams);
$groups = $googleGroups->getGroups();
That's all :)
I have a web app and trying to find out which users in google domain have installed my app. I've tried to use code from here: Determine if a google user's domain has my marketplace app installed, but it doesn't works. I am still getting error "(403) Not authorized to access the application ID" in response.
Code:
$private_key = file_get_contents('path_to_p.12_key');
$service_account_name = '{service_acc_name}'; // name from developers console
$cred = new Google_Auth_AssertionCredentials($service_account_name, array('https://www.googleapis.com/auth/appsmarketplace.license'), $private_key);
$client = new Google_Client();
$client->setAssertionCredentials($cred);
$url = "https://www.googleapis.com/appsmarket/v2/licenseNotification/{appID}";
$httpRequest = new Google_Http_Request($url, 'GET');
$httpRequest->setBaseComponent($client->getBasePath());
$httpRequest = $client->getAuth()->sign($httpRequest);
try
{
$result = $client->execute($httpRequest);
}
catch (Exception $e)
{
echo $e->getMessage();
}
I've also added https://www.googleapis.com/auth/appsmarketplace.license scope in project settings in developers console.
I can't get what's wrong.
OK, I've solved it. You must add all scopes to your service account object that used by your app (not just that scopes you are really want to use by service account):
$cred = new Google_Auth_AssertionCredentials($service_account_name, array('https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile', 'https://www.googleapis.com/auth/admin.directory.user.readonly', 'https://www.googleapis.com/auth/admin.directory.user', 'https://www.googleapis.com/auth/appsmarketplace.license'), $private_key);
When using the below code to attempt to list messages received by a brand new gmail account I just set up, I get
[Google_Service_Exception] Error calling GET https://www.googleapis.com/gmail/v1/users/myGmailAccount%40gmail.com/messages: (500) Backend Error
I know the validation portion is working correctly because I'm able to make minor modifications and query the books api as shown in this example. Below is the code I'm using to attempt to query recent messages received...
const EMAIL = 'myGmailAccount#gmail.com';
private $permissions = [
'https://www.googleapis.com/auth/gmail.readonly'
];
private $serviceAccount = 'xxxxxxxxxxxxxxxxx#developer.gserviceaccount.com';
/** #var Google_Service_Gmail */
private $gmail = null;
/** #var null|string */
private static $serviceToken = null;
public function __construct()
{
$client = new Google_Client();
$client->setApplicationName($this->applicationName);
$this->gmail = new Google_Service_Gmail($client);
//authentication
if (isset(self::$serviceToken)) {
$client->setAccessToken(self::$serviceToken);
}
$credentials = new Google_Auth_AssertionCredentials(
$this->serviceAccount,
$this->permissions,
$this->getKey()
);
$client->setAssertionCredentials($credentials);
if($client->getAuth()->isAccessTokenExpired()) {
$client->getAuth()->refreshTokenWithAssertion($credentials);
}
self::$serviceToken = $client->getAccessToken();
}
public function getMessages()
{
$this->gmail->users_messages->listUsersMessages(self::EMAIL);
}
I have granted API access to gmail:
The 500 makes me believe this is an internal error with the gmail API or I'm missing something the PHP client isn't validating.
This one work for me. My understanding is service account doesn't have a mailbox and it's not too sure which mailbox it should work on. So you can't use listUsersMessages() function to get all messages.
So you will need to specify which email address that the service account need to work on.
Make sure that the scope has been allowed on Web App API to
1. Add this line:
$credentials->sub = self::EMAIL;
Before:
$client->setAssertionCredentials($credentials);
2. Then Update your getMessages() to:
$this->gmail->users_messages->listUsersMessages("me");
Hope this helps!
I imagine the account has logged in to the web interface already, right? Have you tried with a separate user? How about doing something like labels list instead? Did you try using the APIs explorer site? https://developers.google.com/apis-explorer/#p/gmail/v1/
Can you provide the first 5 digits of the developer's client ID (it's not secret anyway) so I can try to track down the error?
For this to work you have to impersonate an user account (the service account doesn't have a mailbox, as lthh89vt points out).
If you try to access the mailbox directly you will get the (500) Back End error.
The full code is:
$client_email = '1234567890-a1b2c3d4e5f6g7h8i#developer.gserviceaccount.com';
$private_key = file_get_contents('MyProject.p12');
$scopes = array('https://www.googleapis.com/auth/gmail.readonly');
$user_to_impersonate = 'user#example.org';
$credentials = new Google_Auth_AssertionCredentials(
$client_email,
$scopes,
$private_key,
'notasecret', // Default P12 password
'http://oauth.net/grant_type/jwt/1.0/bearer', // Default grant type
$user_to_impersonate,
);
$client = new Google_Client();
$client->setAssertionCredentials($credentials);
if ($client->getAuth()->isAccessTokenExpired()) {
$client->getAuth()->refreshTokenWithAssertion();
}
$service = new Google_Service_Gmail($client);
$results = $service->users_messages->listUsersMessages('me');
Full instructions can be found on Google APIs Client Library for PHP
I have an issue I can't solve. I am trying to insert an event in a public calendar. I managed to do it but I do not want the user to login so I tried the Service Account method but I can't get it working.
Issue:
Fatal error: Uncaught exception 'Google_Service_Exception' with message 'Error calling POST https://www.googleapis.com/calendar/v3/calendars/rentyourparis.com_h4s4cm7b8e27of4oigpl4651co%40group.calendar.google.com/events?key=AIzaSyA5xDFe5UjuleS-pssxoXTLOsT0ZA6dur0: (403) Access Not Configured. Please use Google Developers Console to activate the API for your project.' in /home/rentyour/www/google-api-php-client/src/Google/Http/REST.php:79 Stack trace: #0 /home/rentyour/www/google-api-php-client/src/Google/Http/REST.php(44): Google_Http_REST::decodeHttpResponse(Object(Google_Http_Request)) #1 /home/rentyour/www/google-api-php-client/src/Google/Client.php(505): Google_Http_REST::execute(Object(Google_Client), Object(Google_Http_Request)) #2 /home/rentyour/www/google-api-php-client/src/Google/Service/Resource.php(195): Google_Client->execute(Object(Google_Http_Request)) #3 /home/rentyour/www/google-api-php-client/src/Google/Service/Calendar.php(1459): Google_Service_Resource->call('insert', Array, 'Google_Service_...') #4 /h in /home/rentyour/www/google-api-php-client/src/Google/Http/REST.php on line 79
• The calendar API is ON
• I used the Service Accounts credentials
• I removed all referrers
Here is the code :
<?php
session_start();
$path = 'google-api-php-client/src/';
$oldPath = set_include_path(get_include_path() . PATH_SEPARATOR . $path);
require_once 'Google/Client.php';
require_once 'Google/Service/Calendar.php';
define('SERVICE_ACCOUNT_NAME', 'XXXXXX-50dv2bprcmuhn6tuicavg2oqc3c692ua#developer.gserviceaccount.com');
/* THE PATH TO THE SECRET KEY GENERATED WHEN YOU REQUESTED THE
* SERVICE ACCOUNT. The key's name contains it's public key
* fingerprint, represented below by the string <longhexstring>
*/
define('KEY_FILE', 'XXXXXXXXXXXXXX.p12');
$client = new Google_Client();
$client->setApplicationName("My TEST Application");
/* Note: make sure to call $client->setUseObjects(true) if you want to see
* objects returned instead of data (this example code uses objects)
*/
/* If you already have a session token, use it. Normally this token is
* stored in a database and you'd need to retrieve it from there. For
* this demo we'll simply start a new session each time.
*/
session_start();
if (isset($_SESSION['token'])) {
$client->setAccessToken($_SESSION['token']);
}
/* Load the key in PKCS 12 format - remember: this is the file you had to
* download when you created the Service account on the API console.
*/
$key = file_get_contents(KEY_FILE);
$client->setAssertionCredentials(new Google_Auth_AssertionCredentials(
SERVICE_ACCOUNT_NAME,
array('https://www.googleapis.com/auth/calendar'),
$key)
);
if (isset($_SESSION['token'])) {
$client->setAccessToken($_SESSION['token']);
}
/* ------------------------- We are now properly authenticated ------------------- */
$cal = new Google_Service_Calendar($client);
$event = new Google_Service_Calendar_Event();
$event->setSummary('Dinner at Henks house');
/* what to do, summary of the appointment */
$event->setLocation('Slochteren'); /* yes, it exists */
/* Now, set the start date/time
*/
$start = new Google_Service_Calendar_EventDateTime();
$start->setDateTime('2014-08-10T19:00:00.000+01:00'); /* Or e.g. 2010-08-26T10:40:00+02:00 */
$event->setStart($start);
/* Now, set the end date/time
*/
$end = new Google_Service_Calendar_EventDateTime();
$end->setDateTime('2014-08-10T22:00:00.000+01:00'); /* 2010-08-26T10:40:00+02:00 */
$event->setEnd($end);
/* For now I just set one attendee, but you can create lists of many if you want to
*/
$attendee1 = new Google_Service_Calendar_EventAttendee();
$attendee1->setEmail('test#gmail.com');
$attendees = array($attendee1);
$event->attendees = $attendees;
/* CREATE THE EVENT IN THE PROPER CALENDAR
*/
$createdEvent = $cal->events- >insert('XXXXXXXXXXXgpl4651co#group.calendar.google.com', $event);
print "<html><body>";
echo "<pre>I created a calendar entry, it's id is '" . $createdEvent->id . "'</pre>";
print "</body></html>";
/* Here should be some code to store the token in the database again.
* Just to reminds us we put some useless code here ;-P
*/
if ($client->getAccessToken()) {
$_SESSION['token'] = $client->getAccessToken();
}
?>
I've been searching and testing for hours but nothing works fine :(
Thanks for your help
Even though the calendar is public, that does not mean anyone can write into it. For that you will either need to:
Share this public calendar with your service account.
Create an event on the service account's calendar and invite the public calendar.
Or if the calendar is domain owned you can set up your service account to be a friend of the domain (https://developers.google.com/drive/web/delegation).
I authorised the Service Accounts client_id in the Admin console of Google Apps and also deleted from the API Console the IPs in Referees tab. It worked for me. Hope it will helps
I am using gdata to add contacts to my gmail account using the Contact API. When I run the code on my development box as localhost, everything works fine. But when I move the code to a production server (www.somedomain.com) I get "ERROR:Authentication with Google failed. Reason: BadAuthentication".
Here's my code:
<?php
// load Zend Gdata libraries
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Http_Client');
Zend_Loader::loadClass('Zend_Gdata_Query');
Zend_Loader::loadClass('Zend_Gdata_Feed');
// set credentials for ClientLogin authentication
$user = "something#gmail.com";
$pass = "somepassword";
try {
// perform login and set protocol version to 3.0
$client = Zend_Gdata_ClientLogin::getHttpClient(
$user, $pass, 'cp');
$gdata = new Zend_Gdata($client);
$gdata->setMajorProtocolVersion(3);
// create new entry
$doc = new DOMDocument();
$doc->formatOutput = true;
$entry = $doc->createElement('atom:entry');
$entry->setAttributeNS('http://www.w3.org/2000/xmlns/' ,
'xmlns:atom', 'http://www.w3.org/2005/Atom');
$entry->setAttributeNS('http://www.w3.org/2000/xmlns/' ,
'xmlns:gd', 'http://schemas.google.com/g/2005');
$entry->setAttribute('xmlns', 'http://www.w3.org/2005/Atom');
$entry->setAttribute('xmlns:gd', 'http://schemas.google.com/g/2005');
$entry->setAttribute('xmlns:gContact', 'http://schemas.google.com/contact/2008');
$doc->appendChild($entry);
// add name element
$name = $doc->createElement('gd:name');
$entry->appendChild($name);
$fullName = $doc->createElement('gd:fullName', 'Jack Frost');
$name->appendChild($fullName);
// add email element
$email = $doc->createElement('gd:email');
$email->setAttribute('address' ,'jack.frost#example.com');
$email->setAttribute('rel' ,'http://schemas.google.com/g/2005#home');
$entry->appendChild($email);
// add org name element
$org = $doc->createElement('gd:organization');
$org->setAttribute('rel' ,'http://schemas.google.com/g/2005#work');
$entry->appendChild($org);
$orgName = $doc->createElement('gd:orgName', 'Winter Inc.');
$org->appendChild($orgName);
//add to Friends list
$group = $doc->createElement('gContact:groupMembershipInfo');
$group->setAttribute('deleted' ,'false');
$group->setAttribute('href' ,'http://www.google.com/m8/feeds/groups/' .urlencode($user) . '/base/[groupid]');
//addd to my contacts
$entry->appendChild($group);
$group = $doc->createElement('gContact:groupMembershipInfo');
$group->setAttribute('deleted' ,'false');
$group->setAttribute('href' ,'http://www.google.com/m8/feeds/groups/' .urlencode($user) . '/base/6'); //adds to Friends
$entry->appendChild($group);
// insert entry
$entryResult = $gdata->insertEntry($doc->saveXML(),
'http://www.google.com/m8/feeds/contacts/default/full');
echo '<h2>Add Contact</h2>';
echo 'The ID of the new entry is: ' . $entryResult->id;
echo 'The link is: ".$entryResult->getLink('edit')."";
} catch (Exception $e) {
die('ERROR:' . $e->getMessage());
}
?>
Should I be using OAuth for Services instead? I haven't been able to find documentation on how to do this with Zend Gdata.
EDIT - ANSWER FOUND:
Ok - I still have a question about a better auth method but I figured out what the issue was in this case. Google was suspicious of my application logging in. When I checked gmail, I had received the following:
Hi Info,
Someone recently used your password to try to sign in to your Google Account [someone]#gmail.com. This person was using an application such as an email client or mobile device.
We prevented the sign-in attempt in case this was a hijacker trying to access your account. Please review the details of the sign-in attempt:
Saturday, November 2, 2013 9:43:52 PM UTC
IP Address: [xxx.xxx.xx.xxx] ([xxx.xxx.xx.xxx].unifiedlayer.com.)
Location: Unknown
If you do not recognize this sign-in attempt, someone else might be trying to access your account. You should sign in to your account and reset your password immediately.
Reset password
If this was you, and you are having trouble accessing your account, complete the troubleshooting steps listed at http://support.google.com/mail?p=client_login
Sincerely,
The Google Accounts team
I went into gmail security (through the link provided) and was able to authorize the app. Problem solved. Works perfectly now.
I'd still like some pointers on how to use my Client ID, Service Account and Private Key with Zend (if possible). I have connected using the Google API php library but I can't see how to use this within the Zend framework.
On my side, I solved the problem by entering my full email address as username (username#gmail.com).
Previously, I tried to access to my account only with the username...