Gmail API Signature Server Side - php

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?

Related

How do i create permission on google sheet on docs in PHP

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.

Add Post With Google Api to Blogger With Service account keys

I'm trying to create php script that post article to Blogger using Google API. I'm using library google/apiclient:^2.0. I'm choosing to use service account keys. Here's my codes:
require_once '../../vendor/autoload.php';
try {
putenv('GOOGLE_APPLICATION_CREDENTIALS=' . __DIR__ . '/../../credentials/BeMine92929929.json');
$client = new Google_Client();
$client->setApplicationName('Be Mine');
$client->useApplicationDefaultCredentials();
$client->addScope(Google_Service_Blogger::BLOGGER);
$token = $client->fetchAccessTokenWithAssertion();
$client->setAccessToken($token);
// I dump $token here, and it's available. token generation is success.
$service = new Google_Service_Blogger($client);
$blog = $service->blogs->getByUrl('http://bemine.blogspot.com');
//this is part for sending post into blogger, which is getting
//error
$post = new Google_Service_Blogger_Post();
$post->setTitle('Coba Post Artikel Kedua');
$post->setContent('Artikel kedua ini dikirim melalui layanan Blogger api.');
$service->posts->insert('Here Is My Blog ID', $post);
//If I comment posting part, and try to get list article of
//my blog, It success.
$posts = $service->posts->listPosts($blog->getId());
//file_put_contents('/tmp/anarky.txt', var_export($posts, true));
} catch (\Exception $ex) {
echo $ex->getMessage();
}
The response when I try to post article :
{
"error": {
"errors": [
{
"domain": "global",
"reason": "forbidden",
"message": "We're sorry, but you don't have permission to access this resource."
}
],
"code": 403,
"message": "We're sorry, but you don't have permission to access this resource."
}
}
What do I miss Need your advise, thank you.

Google Cloud Storage with Service account - 403 Forbidden

I'm implementing a cron fetching automatically my application installation stats on Google Play Store. So, I need to download a file from Google Cloud Storage and parse it. I made this code :
// Set scopes to authorize full control of Cloud Storage
$scopes = array("https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/devstorage.full_control");
// Create new Google Client
$client = new Google_Client();
// Set path to ServiceAccount's credentials and link them to Google Client
putenv('GOOGLE_APPLICATION_CREDENTIALS=../../libs/GoogleAPICredentials/client_credentials_CPTS.json');
$client->useApplicationDefaultCredentials();
$client->setScopes($scopes);
// Create new Google Service Storage object to handle our repository
$service = new Google_Service_Storage($client);
// Get content of GCS repository
$request = $service->objects->listObjects("MY_BUCKET");
// Settings folders, files, buckets, names
$sourceBucket = "pubsite_prod_rev_BUCKET_IDENTIFIER";
$sourceObject = "installs_MY_APP_IDENTIFIER_" . $date->format("Ym") . "_overview.csv";
$localFile = "../files/temp/installs_MY_APP_IDENTIFIER_" . $date->format("Ym") . "_overview.csv";
$fullSrcObj = "stats/installs/" . $sourceObject;
$destinationBucket = "MY_BUCKET";
$destinationObject = $sourceObject;
// Create new Google Service Storage StorageObject object to handle a single object (file)
$postBody = new Google_Service_Storage_StorageObject($client);
try {
// Copy stats app .CSV file from orignal repository (Google Play Store) to our bucket
$response = $service->objects->copy($sourceBucket, $fullSrcObj, $destinationBucket, $destinationObject, $postBody);
// Get the new object
try {
$object = $service->objects->get($destinationBucket, $destinationObject);
}
catch (Google_Service_Exception $e) {
if ($e->getCode() == 404)
echo ("[<b><span style=\"color:#DA4F34\">ERROR</span></b>] <b><span style=\"color:#DA4F34\">". $e->getMessage() ."</span></b><br><br>");
throw $e;
}
// Download the new object on the MY_COMPAGNY server
$uri = sprintf('https://storage.googleapis.com/%s/%s?alt=media&generation=%s', $destinationBucket, $destinationObject, $object->generation);
$http = $client->authorize();
$response = $http->get($uri);
if ($response->getStatusCode() != 200) {
echo ("[<b><span style=\"color:#DA4F34\">ERROR</span></b>] <b><span style=\"color:#DA4F34\">An unhandled error append !</span></b><br><br>");
die();
}
// Save it into local file on MY_COMPAGNY server
file_put_contents($localFile, $response->getBody());
// Convert .CSV into php array
$csv = array_map('str_getcsv', file($localFile));
// Delete local file
unlink($localFile);
// Remove last line of CSV (Array) --> Empty - And remove first line of CSV (Array) --> Title line
array_shift($csv);
array_pop($csv);
// Insert each line into MY_COMPAGNY Database
$success = false;
foreach ($csv as $row) {
$success = insertAppInstallData(filter_var($row[0], FILTER_SANITIZE_STRING), (int)filter_var($row[$columnTotal], FILTER_SANITIZE_NUMBER_INT), (int)filter_var($row[$columnCurrent], FILTER_SANITIZE_NUMBER_INT))?true: $success;
}
if (!$success)
echo ("[<b><span style=\"color:#DA4F34\">ERROR</span></b>] <b><span style=\"color:#DA4F34\">DataBase insertion failure</span></b><br><br>");
}
catch (Google_Service_Exception $E) {
echo ("[<b><span style=\"color:#DA4F34\">ERROR</span></b>] <b><span style=\"color:#DA4F34\">".$E->getMessage()." - ".$E->getTraceAsString()."</span></b><br><br>");
}
With an user account, it's work well but with a Service account, I have a 403 error, Forbidden on the call to $service->objects->copy().
{
"error":
{
"errors": [
{
"domain": "global",
"reason": "forbidden",
"message": "Forbidden"
}],
"code": 403,
"message": "Forbidden"
}
}
I checks twice if there are good credentials, IAM authorizations, OAuth client, ... and it still dosent work.
I'm not on a Google Engine, I running on a personal Engine.
Someone have an idea ?
Have a good day and Happy New Year !
Ben.
The reason you are getting error 403 is because you are missing the domain wide delegation and the user impersonation step. Please refer to the documentation here to understand how you can access user data with service accounts.
The process is like this:
$scopes = array("https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/devstorage.full_control");
$client = new Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope($scopes); //Set scopes to client object
$client->setSubject("user#domain.com"); // replace with the user account
You are able to make it work with a user account because the user already has access to the resource but not the service account, hence the error 403 forbidden. Unfortunately, the domain wide delegation process and user impersonation only works with G Suite accounts( formerly Google Apps ). Another alternative would be to share the resources with the service account but that is something that I havenĀ“t explored yet and not sure if it is possible. You can also refer to the PHP Client Lib documentation here. I hope this information is helpful.

Update signature of user in my G suite domain

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!

Set publish rights for gmail in google pubsub gives permission denied

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

Categories