I need a service to manipulate emails in a gmail account within a project developed in PHP Symfony...
I found this example : https://github.com/googleapis/google-api-php-client/blob/main/docs/oauth-server.md
But more confusing than helping...
I wrote this code :
src/Service/Gmail.php
<?php
namespace App\Service;
use Google\Client;
class Gmail
{
private \Google\Service\Gmail $api;
private function getGoogleClient(): Client
{
$credentialsPath = getenv('GOOGLE_APPLICATION_CREDENTIALS');
if (empty($credentialsPath)) {
throw new \Exception('You need to set env var GOOGLE_APPLICATION_CREDENTIALS');
}
if (!file_exists($credentialsPath)) {
throw new \Exception('Credentials file path ' . getenv('GOOGLE_APPLICATION_CREDENTIALS') . ' set in GOOGLE_APPLICATION_CREDENTIALS does not exist');
}
$client = new Client();
$client->useApplicationDefaultCredentials();
return $client;
}
private function getApi(): \Google\Service\Gmail
{
if (!isset($this->api)) {
$this->api = new \Google\Service\Gmail($this->getGoogleClient());
}
return $this->api;
}
public function getUserMessages($userId): \Google\Service\Gmail\ListMessagesResponse
{
return $this->getApi()->users_messages->listUsersMessages($userId);
}
}
Then I followed the steps described in Google Workspace for developers :
Create a new project : https://developers.google.com/workspace/guides/create-project?hl=en
Enable Gmail API : https://developers.google.com/workspace/guides/enable-apis?hl=en
Create credentials for web application server-side : https://developers.google.com/workspace/guides/create-credentials?hl=en
But at this point I have no idea what type of credentials I need : "OAuth client ID" or a "Service account" ??? If I choose "Oauth client ID" I suppose I have to use the application type "Web application" with server side url ? Or do I choose "Service account" ?
With service account I got this error :
In REST.php line 134:
{
"error": {
"code": 401,
"message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.
google.com/identity/sign-in/web/devconsole-project.",
"errors": [
{
"message": "Login Required.",
"domain": "global",
"reason": "required",
"location": "Authorization",
"locationType": "header"
}
],
"status": "UNAUTHENTICATED",
"details": [
{
"#type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "CREDENTIALS_MISSING",
"domain": "googleapis.com",
"metadata": {
"service": "gmail.googleapis.com",
"method": "caribou.api.proto.MailboxService.ListMessages"
}
}
]
}
}
You have not proeprly authorized your service account to delegate to a user on your domain.
require_once('../../vendor/autoload.php');
// Some user within your workspace domain
$user_to_impersonate = "your#domain.com";
$sender = $user_to_impersonate;
$to = 'another#domain.com';
$subject = 'Hello';
$messageText = 'How are you doing?';
// The path to your service account credentials goes here.
putenv("GOOGLE_APPLICATION_CREDENTIALS=credentials.json");
$client = new Google_Client();
$client->useApplicationDefaultCredentials();
$client->setSubject($sender);
$client->setApplicationName("Quickstart");
$client->setScopes(["https://mail.google.com/"]);
$service = new Google_Service_Gmail($client);
Related
How do i create permission on google sheet on docs. Here it's show the error
{
"error": {
"errors": [
{
"domain": "global",
"reason": "insufficientPermissions",
"message": "Insufficient Permission"
}
],
"code": 403,
"message": "Insufficient Permission"
}
}
here my code. i use php library.
function insertPermission($fileId) {
$client = $this->getClient();
$client->setScopes(Google_Service_Drive::DRIVE);
$service = new Google_Service_Drive($client);
$newPermission = new Google_Service_Drive_Permission(
array(
"role"=> "writer",
"type"=> "domain",)
);
try {
return $service->permissions->create($fileId, $newPermission);
} catch (Exception $e) {
print "An error occurred: " . $e->getMessage();
}
return NULL;
}
After set permisson it's denied and return this error.
The permissions.create method can be used to insert permissions into a file on google drive.
POST https://www.googleapis.com/drive/v3/files/fileId/permissions
{
"role": "writer",
"type": "user",
"emailAddress": "test#test.com"
}
In order to run the above request the currently authenticated user must have been authenticated using the following scopes and of course have permissions on the file already
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/drive.file
insufficientPermissions
Means that the user you are authenticated with either hasn't granted your application permissions to make these changes or they themselves do not have the permissions. First thing i would do would be check the scopes your application has requested and add the scopes needed and re-authenticated the user and try again.
My goal is to update all user email signature from my domain.
I have set up a service account with domain-wide delegation authority.
But I'm stuck with this error:
{
"error": {
"errors": [
{\n
"domain": "global",
"reason": "failedPrecondition",
"message": "Bad Request"
}
],
"code": 400,
"message": "Bad Request"
}
}
I'm using the same request than the one executed by the API explorer. So it should be well formated...
In the API explorer, it isn't properly working either, i'm having this answer :
{
"error": {
"errors": [
{
"domain": "global",
"reason": "forbidden",
"message": "Delegation denied for vivien#mydomain.com"
}
],
"code": 403,
"message": "Delegation denied for vivien#mydomain.com"
}
}
It seems like I have permission problems but I can't figure out why.
Here is my PHP test code for information :
public function updateSignAction(){
putenv('GOOGLE_APPLICATION_CREDENTIALS='.$this->get('kernel')->getRootDir().'/../app/Resources/files/mydomain.json');
$client = new \Google_Client();
$client->useApplicationDefaultCredentials();
$client->setApplicationName("demo");
$client->addScope([
"https://www.googleapis.com/auth/gmail.settings.basic",
"https://www.googleapis.com/auth/gmail.settings.sharing"
]);
//$client->setSubject('vivien#mydomain.com');
$httpClient = $client->authorize();
$response = $httpClient->put(
'https://www.googleapis.com/gmail/v1/users/vivien#mydomain.com/settings/sendAs/test',
[
'json' => [
'signature' => "test-via-api"
]
]
);
return $this->render('AdminBundle:GoogleApi:user/update.html.twig', array(
'response' => $response->getBody()->getContents(),
));
}
You must authenticate to the API. To do this, there are two ways:
Use OAuth - the Server redirects the user to google's servers, where they can login, grant permission to your app, and pass a token back to you
Service Accounts. These are a little bit more complicated:
First, you'll have to setup an app (done)
Second, you'll have to setup a service account. This is how your app authenticates to google. you've done that, and the certificate you've got contains the private key to authenticate
Third, the user needs to grant your application access to act on behalf of them. This is the point you haven't done yet.
So what you're currently trying is to send mails from the service account, but this is not an Gmail Account.
Please also note: With regular GMail Accounts, you can not use 'Service Accounts'. You'll have to use OAuth. To use Service Accounts, you need to be a Google Apps customer.
To grant your Service Account Permissions to send mails on behalf of your GMails/Google Apps accounts, please follow this document. For One or More API Scopes, you'll have to enter https://mail.google.com/,https://www.googleapis.com/auth/gmail.modify,https://www.googleapis.com/auth/gmail.compose,https://www.googleapis.com/auth/gmail.send.
After you've setup this, it's possible to send mails, just modify the code as follows:
$results = $service->users_messages->send("me", $msg);
won't work, because 'me' referrs to the service account, which can't send mail (see above). Replace me with the user id (mail-address) of the account from which the mails should be send.:
$results = $service->users_messages->send("senders_mail#domain.com", $msg);
Then, you'll need to add
$cred->sub = 'senders_mail#domain.com';
below
$cred = new \Google_Auth_AssertionCredentials(
$service_account_name,
array('https://www.googleapis.com/auth/gmail.send', 'https://www.googleapis.com/auth/gmail.compose'),
$key
);
<?php
require_once realpath(dirname(__FILE__) . '/../src/Google/autoload.php');
$client_id = '*censored*.apps.googleusercontent.com';
$service_account_name = '*censored*#developer.gserviceaccount.com';
$key_file_location = '/tmp/apiKey.p12';
$userid_from='*censored*';
$client = new \Google_Client();
$client->setApplicationName("Client_Library_Examples");
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/gmail.send', 'https://www.googleapis.com/auth/gmail.compose', 'https://www.googleapis.com/auth/gmail.modify','https://www.googleapis.com/auth/gmail.readonly'),
$key
);
$cred->sub=$userid_from;
$client->setAssertionCredentials($cred);
if ($client->getAuth()->isAccessTokenExpired()) {
$client->getAuth()->refreshTokenWithAssertion($cred);
}
$mime = "*censored*";
$service = new \Google_Service_Gmail($client);
$msg = new \Google_Service_Gmail_Message();
$msg->setRaw($mime);
try {
$results = $service->users_messages->send($userid_from, $msg);
print 'Message with ID: ' . $results->id . ' sent.';
} catch (\Exception $e) {
print 'An error occurred: ' . $e->getMessage();
}
If there are any questions left, feel free to ask!
I want to change all signatures from my Gmail domain. This domain has many accounts, and I need to change it from server-side.
I'm using php, and I started my project with:
php composer.phar require google/apiclient:2.0
I wrote one code, but when I try to update one email (like teste#mydomain.com), I receive:
{ "error": { "errors": [ { "domain": "global", "reason": "insufficientPermissions", "message": "Insufficient Permission" } ], "code": 403, "message": "Insufficient Permission" } }
My code (using API client library) is something like:
<?php
// initialize gmail
function getService() {
try {
include_once __DIR__ . '/vendor/autoload.php';
$client = new Google_Client();
$credentials_file = __DIR__ . '/credentials/gmailAPI.json';
// set the location manually. Credential server-side
$client->setAuthConfig($credentials_file);
$client->setApplicationName("GmailAPI");
$client->setScopes(['https://apps-apis.google.com/a/feeds/emailsettings/2.0/']);
$gmail = new Google_Service_Gmail($client);
return $gmail;
} catch (Exception $e) {
throw new Exception($e->getMessage());
}
}
function updateSignature(&$gmail) {
try {
// Start sendAs
$signature = new Google_Service_Gmail_SendAs();
// Configure Signature
$signature->setSignature("Any HTML text here.");
// Update account and print answer
var_dump($gmail->users_settings_sendAs->update("someEmail#myDomain.com.br","someEmail#myDomain.com.br",$signature));
} catch (Exception $e) {
throw new Exception($e->getMessage());
}
}
try {
$gmail = getService();
updateSignature($gmail);
} catch (Exception $e) {
echo $e->getMessage();
}
?>
My credential file (gmailAPI.json) is one service account key, and I'm using Google for Work.
I created this credential using one administrator account from this domain.
My credential file is:
{
"type": "service_account",
"project_id": "myProjectId",
"private_key_id": "myPrivateKeyid",
"private_key": "myPrivateKey",
"client_email": "gmailapi#projectid.iam.gserviceaccount.com",
"client_id": "myId",
"auth_uri": "url",
"token_uri": "url",
"auth_provider_x509_cert_url": "url",
"client_x509_cert_url": "url"
}
Edit 1
I changed the scopes as instructed, and now my scopes are:
$client->setScopes(['https://www.googleapis.com/auth/gmail.settings.basic','https://www.googleapis.com/auth/gmail.settings.sharing']);
I also added permision on Google (/AdminHome?chromeless=1#OGX:ManageOauthClients) to my service account key.
I tried API explorer and it works. When i changed the scopes, the error changed to:
{ "error": { "errors": [ { "domain": "global", "reason": "failedPrecondition", "message": "Bad Request" } ], "code": 400, "message": "Bad Request" } }
I'm using this command:
var_dump($gmail->users_settings_sendAs->update("someEmail#myDomain.com.br","someEmail#myDomain.com.br",$signature));
I tried also
var_dump($gmail->users_settings_sendAs->get("someEmail#myDomain.com.br","someEmail#myDomain.com.br"));
But I received same error.
Thank You everyone.
I tried a lot of codes, and finally found one that works.
include_once __DIR__ . '/vendor/autoload.php';
// credential file (service account)
$credentials_file = __DIR__ . '/credentials/gmailAPI.json';
putenv('GOOGLE_APPLICATION_CREDENTIALS='.$credentials_file);
// Initialize Google Client
$client = new Google_Client();
$client->useApplicationDefaultCredentials();
// scopes to change signature
$client->setScopes(['https://www.googleapis.com/auth/gmail.settings.basic','https://www.googleapis.com/auth/gmail.settings.sharing']);
// *important* -> Probably because delegated domain-wide access.
$client->setSubject("admEmailHere#test.com");
// Initialize Gmail
$gmail = new Google_Service_Gmail($client);
// set signature
$signature = new Google_Service_Gmail_SendAs();
$signature->setSignature("HTML code here.");
// update signature
$response = $gmail->users_settings_sendAs->update("admEmailHere#test.com","admEmailHere#test.com",$signature)->setSignature();
// get signature
$response = $gmail->users_settings_sendAs->get("admEmailHere#test.com","admEmailHere#test.com")->getSignature();
echo json_encode($response);
I commented all code, and I used https://developers.google.com/api-client-library/php/auth/service-accounts to create it.
Atention -> You need to give permission on Gmail, and create server key (with domain-wide access).
I think your access token doesn't contain all scopes you want. First try in API explorer. Check what are the scopes API explorer request from you and then add those scopes to your code(place where you get permission).
https://developers.google.com/apis-explorer
hope this solves your problem
Well, the error 403 or Insufficient Permission is returned when you have not requested the proper or complete scopes you need to access the API that you are trying to use. Here is the list of scopes with description that you can use with Gmail API.
To know the scope that you are using, you can verify it with this:
https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=xxxxxx
Note: You need to include all the scope that you are using and make
sure you enable all the API that you use in the Developer Console.
This Delegating domain-wide authority to the service
account
might also help.
For more information, check these related SO questions:
Gmail API: Insufficient Permission
How do I get around HttpError 403 Insufficient Permission?
I am unable to delete a folder (created by another person) even if I try to change the rights, I have a console application and the current authenticated user can do the following :
create folders/files
move files
Scopes:
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive.appdata',
'https://spreadsheets.google.com/feeds',
Tries:
1.Set permissions to the current user
public function deleteFolder(\Google_Service_Drive_DriveFile $folder)
{
$permission = new \Google_Service_Drive_Permission();
$permission->setRole( 'owner' );
$permission->setType( 'user' );
$permission->setEmailAddress('someId#developer.gserviceaccount.com');
$permission = $this->googleDriveClient->permissions->create( $folder->getId(),$permission,array('transferOwnership'=>true));
$this->googleDriveClient->files->delete($folder->getId());
}
Result:
[Google_Service_Exception] {
"error": {
"errors": [
{
"domain": "global",
"reason": "internalError",
"message": "Internal Error"
}
],
"code": 500,
"message": "Internal Error" } }
2.Set permission type to anyone
public function deleteFolder(\Google_Service_Drive_DriveFile $folder)
{
$permission = new \Google_Service_Drive_Permission();
$permission->setRole( 'owner' );
$permission->setType( 'anyone' );
$permission = $this->googleDriveClient->permissions->create( $folder->getId(),$permission,array('transferOwnership'=>true));
$this->googleDriveClient->files->delete($folder->getId());
}
Result:
[Google_Service_Exception]
{
"error": {
"errors": [
{
"domain": "global",
"reason": "insufficientFilePermissions",
"message": "The user does not have sufficient permissions for this file ."
}
],
"code": 403,
"message": "The user does not have sufficient permissions for this file." }
}
try 3: Impersonate the creator
$this->googleClient->setAccessType('offline');
$this->googleClient->setSubject('x#domain.com');//if removed everything works
[GuzzleHttp\Exception\ClientException]
Client error: POST https://www.googleapis.com/oauth2/v4/token
resulted in a 401 Unauthorized response:
{
"error": "unauthorized_client",
"error_description": "Unauthorized client or scope in request.",
"error_uri": ""
}
Am I missing something?
Only the owner of the folder can delete the folder.
Use a service account Using OAuth 2.0 for Server to Server Applications, take the identity of the owner of the folder Perform Google Apps Domain-Wide Delegation of Authority.
Once authorized as the owner of the folder it should be possible to delete the folder.
I'm trying to set publish permissions for gmail to a pubsub topic in google cloud.
The application where I implemented this code is running in AWS.
It's a PHP application and I'm using version 2.0.0-RC7 of the google PHP api client.
In code, I implemented the flow as described in the documentation:
Create a topic (Works)
Create a subscription (works)
Grant publish rights to gmail (here I get stuck)
The first two actions are done with the same google client instance, that is authenticated with the service account credentials.
The code:
$scopes = [
'https://www.googleapis.com/auth/pubsub',
'https://mail.google.com',
];
$pushEndpoint = 'https://some.url/google_notifications/';
$client = new Google_Client();
$client->setScopes($scopes);
$client->setAuthConfig($serviceAccountInfo);
if ($client->isAccessTokenExpired()) {
$client->refreshTokenWithAssertion();
}
$service = new Google_Service_Pubsub($client);
// This part works
$topicObject = new Google_Service_Pubsub_Topic();
$topicObject->setName($this->getTopicName());
$service->projects_topics->create($this->getTopicName(), $topic);
// This part also works
$push = new Google_Service_Pubsub_PushConfig();
$push->setPushEndpoint($pushEndpoint);
$subscription = new Google_Service_Pubsub_Subscription();
$subscription->setName($this->getSubscriptionName());
$subscription->setTopic($this->getTopicName());
$subscription->setPushConfig($push);
$service->projects_subscriptions->create($this->getSubscriptionName(), $subscription);
// This part gives the error
$binding = new Google_Service_Pubsub_Binding();
$binding->setRole('roles/pubsub.publisher');
$binding->setMembers(['serviceAccount:gmail-api-push#system.gserviceaccount.com']);
$policy = new Google_Service_Pubsub_Policy();
$policy->setBindings([$binding]);
$setRequest = new Google_Service_Pubsub_SetIamPolicyRequest();
$setRequest->setPolicy($policy);
try {
$result = $service->projects_topics->setIamPolicy($this->getTopicName(), $setRequest);
var_dump($result);
} catch (\Exception $e) {
echo $e->getMessage();
}
The result is always:
{
"error": {
"code": 403,
"message": "User not authorized to perform this action.",
"errors": [
{
"message": "User not authorized to perform this action.",
"domain": "global",
"reason": "forbidden"
}
],
"status": "PERMISSION_DENIED"
}
}
Can someone tell me what I'm doing wrong ?
It's really annoying to set those permissions by hand all the time.
Thanks.
To use projects.topics.setIamPolicy method your service account must have a "Pub/Sub Admin" role. You can change a role of the account by visiting "IAM & admin" page for your project: https://console.cloud.google.com/iam-admin/iam