I am trying to list emails from a standard Gmail account (not a Google Apps account) using OAuth2 and a Service Account (actually, I want to send emails – but that can wait).
I have created the Project, created the service account, downloaded the private key, and enabled the Gmail API (and the Calendar API). I can successfully access my calendar using code very similar to the code below. However, I am receiving the following error when attempting to list mail messages.
Error refreshing the OAuth2 token, message: error_description "Unauthorized client or scope in request."
(For info. If I comment out the $credential->sub = … line, I get the following error: “(500) Backend Error” and I can see that this is logged in the Usage tab of the Developers API page).
Can anyone confirm that what I am trying to do is possible? And if so, any ideas where to go next?
Here is my code:
class mail
{
private $service;
public function __construct($clientid, $keyfile, $account_name, $app_name)
{
$client = new Google_Client();
$client->setApplicationName($app_name);
$this->service = new Google_Service_Gmail($client);
// Load the key in PKCS 12 format
$key = file_get_contents($keyfile);
$client->setClientId($clientid);
$credentials = new Google_Auth_AssertionCredentials(
$account_name, array('https://mail.google.com/'),$key);
$credentials->sub = 'myemailaddress...#gmail.com';
$client->setAssertionCredentials($credentials);
}
public function list_mail()
{
try
{
$result = $this->service->users_messages->listUsersMessages("me");
}
catch (Exception $e)
{
throw new Exception("Gmail API error: ".$e->getMessage()."<br />");
}
return $result;
}
}
$mail_obj = new mail(CLIENT_ID, KEY_FILE, SERVICE_ACCOUNT_NAME, APP_NAME);
$result = $mail_obj->list_mail();
Related
I a trying to get the latest emails from my GMAIL inbox via the GMAIL REST API v2 and using a service account key (a solution for server to server communication). However, I get the following error:
{ "error": "invalid_scope", "error_description": "Empty or missing scope not allowed." }
Below is my PHP code. I have used composer to install the google API PHP client.
Has anyone encountered and solved this issue?
<?php
require_once 'vendor/autoload.php';
$scopes = array('https://www.googleapis.com/auth/gmail.readonly');
$client = new Google_Client();
$client->setAuthConfig('serviceaccount-xxxxxxxxx.json');
try{
$service = new Google_Service_Gmail($client);
$opt_param['maxResults'] = 5; // Return Only 5 Messages
$opt_param['labelIds'] = 'INBOX'; // Only show messages in Inbox
$messages = $service->users_messages->listUsersMessages('me',$opt_param);
$list = $messages->getMessages();
var_dump($list);
} catch (Exception $e) {
print($e->getMessage());
}
?>
First of all I want to say that I read all the other post about this same problem and I couldn't find a solution.
This is the error:
exception 'Google_Auth_Exception' with message 'Error fetching OAuth2 access token, message: 'invalid_grant: Code was already redeemed.
In localhost, it works perfect. I can debug it and I get Google plus && emails info without problem.
I create a new credential for production (Web App) and I set my oauth return uri in: Authorized redirect URI
(http://www.__myweb_.com/plus_gmail/oauth2callback)
It works, because I can accept and I get the code in callback, but it fails when I try to authenticate;
I also tried to revokeToken and my Session is empty...
How can I solve this problem??
Thanks a lot!!
This is my code:
function __construct()
{
define('CLIENT_SECRET_PATH', 'client_secret_online.json');
$this->redirect_url=base_url()."plus_gmail/oauth2callback";
$this->client = new Google_Client();
$this->client->setIncludeGrantedScopes(true);
$this->client->setAuthConfigFile(CLIENT_SECRET_PATH);
$this->client->addScope(Google_Service_Plus::PLUS_LOGIN);
$this->client->addScope(Google_Service_Plus::PLUS_ME);
$this->client->addScope(Google_Service_Plus::USERINFO_EMAIL);
$this->client->addScope(Google_Service_Plus::USERINFO_PROFILE);
$this->client->addScope(Google_Service_Gmail::MAIL_GOOGLE_COM);
$this->client->setRedirectUri($this->redirect_url);
$this->client->setAccessType('offline');
$this->client->setIncludeGrantedScopes(true);
}
function index()
{
$auth_url = $this->client->createAuthUrl();
header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
}
function oauth2callback()
{
if (! isset($_GET['code']))
{
echo "Error getting code"
}
else
{
$token=$_GET['code'];
$data["token"]=$token;
try
{
$this->client->authenticate($token);
$access_token = $this->client->getAccessToken();
$plus = new Google_Service_Plus($this->client);
$me=$plus->people->get('me');
$this->daouser->newUserPlus($me,$access_token);
var_dump($me);
$this->readEmailsId($this->client);
}
catch (Exception $e)
{
var_dump($e)
}
}
}
I also need to know why it shows me the var_dump($e), but I have a new row in my database withe user google plus info.
I delete access to my app in my gmail account (https://security.google.com/settings/security/permissions) and give access again getting the same error (Code was already redeemed). I used a new gmail account and I get also the same error...
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 am new to Google Drive and have uploaded many files. I have changed the status of all files to be shared if the download link is known.
I would like to use PHP to generate a list of all of the direct download links for the files. The PHP script is run on my localhost and I just need the array to be saved to a text file to be used later.
I have had a very hard time trying to get Oauth working but came across this script that looks like what I need. I set up my service account and have my service account email and .p12 file.
I downloaded the Google PHP client code base and set up a test script on my localhost. This is what I have
require_once "Google/Client.php";
require_once "Google/Service/Drive.php";
require_once "Google/Service/Oauth2.php";
require_once "Google/Auth/AssertionCredentials.php";
session_start();
function buildService($userEmail) {
$SERVICE_ACCOUNT_EMAIL = '12345#developer.gserviceaccount.com';
$SERVICE_ACCOUNT_PKCS12_FILE_PATH = '12345-privatekey.p12';
$key = file_get_contents($SERVICE_ACCOUNT_PKCS12_FILE_PATH);
$auth = new Google_AssertionCredentials(
$SERVICE_ACCOUNT_EMAIL,
array('https://www.googleapis.com/auth/drive'),
$key);
$auth->sub = $userEmail;
$client = new Google_Client();
$client->setUseObjects(true);
$client->setAssertionCredentials($auth);
return new Google_DriveService($client);
}
//... function retrieveAllFiles($service)
$service = buildService("myemail#gmail.com");
$allFiles = retrieveAllFiles($service);
print_r($allFiles);
I am working off of this example
http://stackoverflow.com/questions/21046631/google-drive-api-php-cant-list-files
and this
https://developers.google.com/drive/web/delegation#instantiate_a_drive_service_object
I am unsure what to do, I have added the required libraries but the following functions are coming up as being unknown in the PHP script
Google_AssertionCredentials
setUseObjects
Google_DriveService
retrieveAllFiles
Am I missing an obvious library? They are named different in the example but I'm guessing the base names have changed since there were updates... I have spent a lot of time reading up on Google Drive and Oauth without any luck. My only goal with this script is to get a list of the direct download links. I can do it manually but there are too many files.
Any help would be great.
Thanks.
* EDIT: *
So I this is what I have tried to obtain my token:
I am following this quick start guide:
https://developers.google.com/drive/web/quickstart/quickstart-php
Here is my code
<?php
require_once 'Google/client.php';
require_once 'Google/Service/drive.php';
defined('STDIN') or define('STDIN', fopen('php://stdin', 'r')); //I had to add this part as I was getting an undefined error for STDIN
$client = new Google_Client();
// Get your credentials from the console
$client->setClientId('xxx.apps.googleusercontent.com');
$client->setClientSecret('xxx');
$client->setRedirectUri('http://localhost/test/fetch.php'); //same as registered one
$client->setScopes(array('https://www.googleapis.com/auth/drive'));
$service = new Google_Service_Drive($client);
$authUrl = $client->createAuthUrl();
//Request authorization
print "Please visit:\n$authUrl\n\n";
print "Please enter the auth code:\n";
$authCode = trim(fgets(STDIN));
// Exchange authorization code for access token
$accessToken = $client->authenticate($authCode);
$client->setAccessToken($accessToken);
This results in error
Fatal error: Uncaught exception 'Google_Auth_Exception' with message 'Invalid code' in C:\Apache24\htdocs\test\Google\Auth\OAuth2.php:95 Stack trace: #0 C:\Apache24\htdocs\test\Google\Client.php(135): Google_Auth_OAuth2->authenticate('') #1 C:\Apache24\htdocs\test\new.php(26): Google_Client->authenticate('') #2 {main} thrown in C:\Apache24\htdocs\test\Google\Auth\OAuth2.php on line 95
Any thoughts ?
Thanks.
ANOTHER EDIT
So I have reverted to the old Drive PHP library as the new Drive PHP library has no documentation or examples.
This is my attempt to obtain a token and fetch the direct download file links
require_once 'google-old/google-api-php-client/src/Google_Client.php';
require_once 'google-old/google-api-php-client/src/contrib/Google_DriveService.php';
$client = new Google_Client();
// Get your credentials from the console
$client->setClientId('xx.apps.googleusercontent.com');
$client->setClientSecret('xx');
$client->setRedirectUri('urn:ietf:wg:oauth:2.0:oob');
$client->setScopes(array('https://www.googleapis.com/auth/drive'));
$service = new Google_DriveService($client);
$authUrl = $client->createAuthUrl();
//Request authorization
print "Please visit:\n$authUrl\n\n";
print "Please enter the auth code:\n";
$authCode = trim(fgets(STDIN));
// Exchange authorization code for access token
$accessToken = $client->authenticate($authCode);
$client->setAccessToken($accessToken);
$getAll = retrieveAllFiles($service);
print "<pre>";
print_r($getAll);
print "</pre>";
/**
* Retrieve a list of File resources.
*
* #param Google_DriveService $service Drive API service instance.
* #return Array List of Google_DriveFile resources.
*/
function retrieveAllFiles($service) {
$result = array();
$pageToken = NULL;
do {
try {
$parameters = array();
if ($pageToken) {
$parameters['pageToken'] = $pageToken;
}
$files = $service->files->listFiles($parameters);
$result = array_merge($result, $files->getItems());
$pageToken = $files->getNextPageToken();
} catch (Exception $e) {
print "An error occurred: " . $e->getMessage();
$pageToken = NULL;
}
} while ($pageToken);
return $result;
}
The problem I now face is that I am stuck at the "Paste the code into your app" part.
The
$authCode = trim(fgets(STDIN));
doesn't seem to want to execute.
Also I believe the file fetching code will just fetch the file names, and not the direct download links. I could not find an example for this.
Any suggestions would be much appreciated.
Thanks.
I am testing this on my localhost.
Firstly, separate your learning about OAuth from your learning about Drive. As a combined problem, it's hard, but as two separate problems, it's pretty simple.
For OAuth, everything you need to know is on this one web page https://developers.google.com/accounts/docs/OAuth2WebServer
Having read that page and understood it, if you want to use a library to make the calls, go ahead. IMHO the OAuth libraries don't do a great job, but ymmv. With or without a library, it is essential that you understand what is happening.
Having cracked OAuth, you end up with a shiny access_token which you drop into the Drive API, either as an http header if you are calling the rar API, or wrapped in a credential object if you are using one of the libraries.
Replace the STDIN stuff.
put this in instead:
if (!isset($_GET['code'])) {
print "Please visit:\n$authUrl\n\n";
print "Please enter the auth code:\n";
} else { $authCode = ''; }
//$authCode = trim(fgets(STDIN));
$authCode = $_GET['code'];
Google's code is buggy in terms of their thing. Having done so I'm getting file creation (it's still pumping out an error but it's producing the files).
replace their file_get_contents line with:
$data = 'blahblah blah';//file_get_contents('document.txt');
and there you go.