This should be strange moment when uploading files with Service account to my google drive is not showing inside the google drive.
I read somewhere the service account files will not show in my google drive. then what is the point of service account ? and how can i upload files in my google drive account via service account ?
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
require __DIR__ . '/vendor/autoload.php';
putenv('GOOGLE_APPLICATION_CREDENTIALS=credentials.json');
$client = new Google\Client();
if (getenv('GOOGLE_APPLICATION_CREDENTIALS')) {
// use the application default credentials
$client->useApplicationDefaultCredentials();
} else {
echo missingServiceAccountDetailsWarning();
return;
}
$client->setApplicationName("Client_Library_Examples");
//$client->setScopes(Google_Service_Drive::DRIVE_METADATA_READONLY);
$client->addScope("https://www.googleapis.com/auth/drive");
$service = new Google_Service_Drive($client);
//Insert a file
$file = new Google_Service_Drive_DriveFile();
$file->setName('test-1.pptx');
$file->setDescription('A test document');
$file->setMimeType('application/vnd.openxmlformats-officedocument.presentationml.presentation');
$data = file_get_contents('test-1.pptx');
$createdFile = $service->files->create($file, array(
'data' => $data,
'mimeType' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'uploadType' => 'multipart'
));
echo '<pre>';
var_dump($createdFile);
// Print the names and IDs for up to 10 files.
$optParams = array(
'pageSize' => 10,
'fields' => 'nextPageToken, files(id, name)'
);
$results = $service->files->listFiles($optParams);
var_dump($results);
Service accounts are dummy users. They have their own google drive account. When you upload a file by default it will go to the service accounts Google drives account.
Now if you want to upload a file to your personal drive account. what you can do is share a folder on your google drive account with the service account basically take the service accounts email address the one with a # in it and share the directory with it though the web app like you would any other user.
Now the service account will instantly have access to it this is called preauthorized. By preauthorizing the service account there will be no need for user interaction to access that folder.
You appear to be trying to create a new file all you need to do is set the parent folder of the file in the metadata before you upload it.
$file->setParents('FolderIdFromYourDrive');
Do a file.list first on the service accounts drive account if you want to find out what folder is is or you can just look at the top in the url bar.
Service accounts are nice if your application has some static data that it needs to access. This can be done by placing the files on drive and then granting the service account access to those files. Service accounts are great because they are preauthorized and you dont need the consent screen to access private user data.
You can also upload the files to the service accounts drive account and share them with your personal google account and you will have access that way.
Answer
A service account is a special kind of account used by an application or a virtual machine (VM) instance, not a person.
Option 1: Use Domain-Wide Delegation
Service accounts can access user's data without any manual authorization on their part using Domain-Wide Delegation of Authority.
With that being said, you can upload files into your Google Drive using a service account. Follow the steps in the documentation to create the service account and credentials. It has three main points:
Create the service account in the Google Cloud Console
Enable the domain-wide delegation in the Google Admin console
Instantiate a Drive service object. Check the second last line:
$client = new Google_Client();
$client->useApplicationDefaultCredentials();
$client->setApplicationName("Client_Library_Examples");
$client->addScope("https://www.googleapis.com/auth/drive");
$client->setSubject('email#domain.com');
$service = new Google_Service_Drive($client);
Option 2: Share the file with your main account
...
$service = new Google_Service_Drive(client)
$role = 'writer';
$userEmail = 'user#gmail.com';
$fileId = 'The ID of the file to be shared';
$userPermission = new Google_Service_Drive_Permission(array(
'type' => 'user',
'role' => $role,
'emailAddress' => $userEmail
));
$request = $service->permissions->create(
$fileId, $userPermission, array('fields' => 'id')
);
Reference
Service accounts
Domain-Wide Delegation of Authority
Google Cloud Console
Related
I'm trying to retrieve data from an AdSense account through the AdSense Management API.
I have successfully retrieved data using Google APIs Explorer when connected with my AdSense account.
I am now trying to get this data from a PHP script which will be run in a crontab.
My understanding is that I need a Service account which I created and I generated a json file.
The following code fails if I try to use the json file with two different errors :
if I use the setSubject with the owner of the AdSense account :
"error": "unauthorized_client",
"error_description": "Client is unauthorized to retrieve access tokens using this method."
If I comment the setSubject :
"reason": "noAdSenseAccount",
"message": "User does not have an AdSense account."
Code:
<?php
require_once '../../vendor/autoload.php';
putenv('GOOGLE_APPLICATION_CREDENTIALS=../conf/Mercury-testlpstats.json');
$client = new Google_Client();
$client->useApplicationDefaultCredentials();
$client->addScope('https://www.googleapis.com/auth/adsense.readonly');
//$client->setSubject('AdTech#Lagardere-Pub.com');
$service = new Google_Service_AdSense($client);
$startDate = '2017-05-01';
$endDate = '2017-05-01';
$optParams = array(
'metric' => array('INDIVIDUAL_AD_IMPRESSIONS', 'EARNINGS'),
'dimension' => 'DATE',
'filter' => array('AD_UNIT_ID==ca-pub-XXXX:YYYY'),
'useTimezoneReporting' => true
);
$report = $service->accounts_reports->generate('pub-ZZZZZ', $startDate, $endDate, $optParams);
2nd attempt with OAuth
I've created a OAuth account in https://console.cloud.google.com/apis/credentials/oauthclient/. When I check the AdSense API, my account appears in the list of authorized users in https://console.cloud.google.com/apis/api/adsense.googleapis.com/overview.
I've downloaded the corresponding JSON and changed my code but it still says:
"insufficientPermissions" if I set $client->useApplicationDefaultCredentials();
"Login Required" otherwise
require_once '../../vendor/autoload.php';
$client = new Google_Client();
$client->setAccessType('online');
$client->setAuthConfigFile('/home/al1/lpstats/conf/client_secret_725834039890-klbuc13f8089rjh7eis439b93n7sqqfv.apps.googleusercontent.com.json');
$client->addScope('https://www.googleapis.com/auth/adsense.readonly');
$service = new Google_Service_AdSense($client);
$startDate = '2017-05-01';
$endDate = '2017-05-01';
$optParams = array(
'metric' => array('INDIVIDUAL_AD_IMPRESSIONS', 'EARNINGS'),
'dimension' => 'DATE',
'filter' => array('AD_UNIT_ID==ca-pub-5035025648894332:3442683203'),
'useTimezoneReporting' => true
);
$report = $service->accounts_reports->generate('pub-5035025648894332', $startDate, $endDate, $optParams);
unauthorized_client
Means that the user you are authencating with does not have access to the Adsence Account you are trying to access.
Service account support
There are a number of Google APIs which don't support service accounts. Most noticeably would be YouTube API which does not even have a way of sharing your account with another user.
There are others which will allow you to share your data with another user but require that the user in question respond to an email notification. Blogger is one and Adsence is apparently another.
Solution
You will need to authenticate using Oauth2 using a user who has access to the adsence account in question. Authenticate your code once save the refresh token and use the refresh token to request a new access token within your cron job.
i have created project in google console with service type credentials. I have downloaded p12 key file, and then wrote sample code for inserting file to test it:
<?php
require_once 'google-api-php-client/src/Google_Client.php';
require_once 'google-api-php-client/src/contrib/Google_DriveService.php';
require_once "google-api-php-client/src/contrib/Google_Oauth2Service.php";
$scopes = array('https://www.googleapis.com/auth/drive');
$accountEmail = 'SECRET';
$accountP12FilePath = './key.p12';
$key = file_get_contents($accountP12FilePath);
$auth = new Google_AssertionCredentials($accountEmail, $scopes, $key);
$client = new Google_Client();
$client->setUseObjects(true);
$client->setAssertionCredentials($auth);
$service = new Google_DriveService($client);
$file = new Google_DriveFile();
$file->setTitle('Test Title');
$file->setDescription('test description');
$parent = new Google_ParentReference();
$file->setParents(array($parent));
$createdFile = $service->files->insert($file, array(
'data' => 'test data',
'mimeType' => 'text/plain'
));
var_dump($createdFile);
?>
It dumps Google_DriveFile object with many urls. Some of them, like 'downloadUrl', returns 401 Unauthorized error. When I tried 'alternateLink' it showed google drive page with information that i need access, and buttons to request access and to switch account.
It also said that Im currently logged on my account, that was beeing used to create project, and that is visible on Permissions page of the project on the google console page.
How can I access drive that belongs to the project I used in the script ?
Finally found solution, it was missing permissions on file. Its worth to note that passing permission object to the $file object (before insertion) didn't work. I had to insert permission by passing already created file ID and permission object to the $service->permissions->insert method, just like that:
$permission = new Google_Permission();
$permission->setValue('PERSONAL.ACCOUNT#gmail.com');
$permission->setType('user');
$permission->setRole('writer');
$service->permissions->insert($createdFile->getId(), $permission);
Still I wasn't able to actually edit this file. When I opened file with URL
$createdFile->getAlternateLink();
Then I had to reopen that file with google docs. That copied file to my personal account and then I could only edit only that copy of a file, not a base file itself.
I dont know if there is a way to make that files truly editable, but I moved to the dropbox, as google drive API and its documentation SUX.
Eventually, I would like to upload a file to my Google Drive via App Engine php script.
However, following tutorials such as https://developers.google.com/drive/web/quickstart/quickstart-php
force the individual to be signed in. I'm assuming that in this case, I would have to use Service Account credentials.
From here, I uploaded the following code along with the needed API Client Library files
<?php
require_once 'autoload.php';
//require_once 'Auth/AssertionCredentials.php';
require_once 'Client.php';
require_once 'Service/Drive.php';
//
$client = new Google_Client();
$client->setApplicationName("blah-blah-00000");
$client->setClientID("000000000000-00000000000000000000000000000000.apps.googleusercontent.com");
$service = new Google_Service_Drive($client);
// This file location should point to the private key file.
$key = file_get_contents("BlahBlah-000000000000.p12");
$cred = new Google_Auth_AssertionCredentials(
"000000000000-00000000000000000000000000000000#developer.gserviceaccount.com",
// Replace this with the scopes you are requesting.
array('https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.file'),
$key
);
$client->setAssertionCredentials($cred);
//Insert a file
$file = new Google_Service_Drive_DriveFile();
$file->setTitle('HelloWorld');
$file->setDescription('A test document');
$file->setMimeType('text/plain');
$data = "Hello World!";
$createdFile = $service->files->insert($file, array(
'data' => $data,
'mimeType' => 'text/plain',
'uploadType' => 'media'
));
//print_r($createdFile);
?>
After visiting the associated appspot.com, the page is blank. At this point, I check the Google Drive associated with the account; however no new files are found.
Am I correct in using a Service account? Provided that this is correct, my next question is am I correctly setting up the credentials? Do I have to impersonate my user account/the account that the App Engine application is setup with?
Am I correct in using a Service account?
Probably not. I see a lot of people using a Service Account when what they should be using is offline access to a standard account.
"At this point, I check the Google Drive associated with the account; however no new files are found". Specifically, people don't realise that a Service Account is NOT associated with their standard account, so naturally when viewing Drive for their standard account, the files from the Service Account aren't there. They're in the Service Account which has no UI.
Provided that this is correct, my next question is am I correctly setting up the credentials?
It isn't. You say " following tutorials such as ... force the individual to be signed in." There aren't any tutorials that show your use case, but that doesn't mean it can't be done. Just that nobody wrote a tutorial on how to do it.
Do I have to impersonate my user account/the account that the App Engine application is setup with?
You need to:-
do a one-time authorize for your account which requests offline access. This will give you a refresh Token which you need to save.
Then use the Refresh Token to request an Access Token when you want to access Drive.
The good news is step 1 doesn't require you to write any code. See How do I authorise an app (web or installed) without user intervention? (canonical ?)
After following pinoyyid's LINK and using an offline OAuth2 refresh token, I updated my code to the following
<?php
require_once 'autoload.php';
require_once 'Client.php';
require_once 'Service/Drive.php';
//
$client = new Google_Client();
// Replace this with your application name.
$client->setApplicationName("00000-00000-00000");
$client->setClientID("000000000000-00000000000000000000000000000000.apps.googleusercontent.com");
$client->setClientSecret("XXXXXXXXXXXXXXXXXXXX");
$client->refreshToken("1/KXXXXXXXXXXXXXXXXXXXXXXXXX");
// Replace this with the service you are using.
$service = new Google_Service_Drive($client);
$_SESSION['service_token'] = $client->getAccessToken();
//Insert a file
$file = new Google_Service_Drive_DriveFile();
$file->setTitle('HelloWorld');
$file->setDescription('A test document');
$file->setMimeType('text/plain');
$data = "Hello World!";
$createdFile = $service->files->insert($file, array(
'data' => $data,
'mimeType' => 'text/plain',
'uploadType' => 'media'
));
?>
I am developing a web API for a mobile app which needs to store files. Currently, the mobile clients send a request to the API with the file's data, and the file is stored directly on the server. However, I need to save space on the server and this is just a temporary solution.
What I would like to do is have the mobile client upload the file to the API, which then uploads that file to Google Drive. I have taken a look at the Google Drive API and I got it to upload a file from my home computer successfully, but in order to do so, I needed to sign in to my google account to authorize the upload. Here is the code:
<?php
require_once 'google-api-php-client/src/Google_Client.php';
require_once 'google-api-php-client/src/contrib/Google_DriveService.php';
$client = new Google_Client();
// Get your credentials from the console
$client->setClientId(' my client id ');
$client->setClientSecret(' my client secret ');
$client->setRedirectUri('https://www.hwassassin.net16.net/oauth2callback');
$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);
//Insert a file
$file = new Google_DriveFile();
$file->setTitle('My document');
$file->setDescription('A test document');
$file->setMimeType('text/plain');
$data = file_get_contents('document.txt');
$createdFile = $service->files->insert($file, array(
'data' => $data,
'mimeType' => 'text/plain',
));
print_r($createdFile);
?>
Since I need to upload the file from the backend, I would like to know if I can somehow programmatically log in to my Google Drive account from the server, without requiring the Client to take any action.
If this is not even possible, I would appreciate any alternative cloud storage API's that don't require the client to sign in to an account.
Any help would be greatly appreciated!
Solution is using application owned accounts using service accounts or regular accounts. Please follow the directions at https://developers.google.com/drive/web/service-accounts.
I want to download mailbox by using the Admin SDK, but I can't get it working. I can't find what's the Scope I need to define. I'm using a service account.
In order to prepare a download, you have to do a POST request to https://apps-apis.google.com/a/feeds/compliance/audit/mail/export/{domain name}/{source user name}, but there is no audit.mail scope or something like that.
Here is my request:
<?php
$client = new \Google_Client();
$cred = new \Google_Auth_AssertionCredentials(
'***#developer.gserviceaccount.com',
array(
'https://apps-apis.google.com/a/feeds/compliance/audit',
),
file_get_contents($path)
);
$client->setAssertionCredentials($cred);
if($client->getAuth()->isAccessTokenExpired()) {
$client->getAuth()->refreshTokenWithAssertion($cred);
}
$url = "https://apps-apis.google.com/a/feeds/compliance/audit/publickey/" . $domain;
$req = new \Google_Http_Request($url, 'POST');
$req->setPostBody($xml);
$token = json_decode($client->getAccessToken(), true);
$req->setRequestHeaders(
array(
'Content-Type'=> 'application/atom+xml; charset=utf-8',
'Authorization'=> 'Bearer ' . $token['access_token'] . '',
)
);?>
But I get a 403 error: You are not authorized to access this API..
What's the best way to download a mailbox using the PHP API with a service account?
The email audit api scope is: https://apps-apis.google.com/a/feeds/compliance/audit/
Did you grant third party client access in your admin console for your service account with the appropriate scope? The scope setting have to be set within your code and also in admin console.
Here is complete instruction on how to correctly set up a service account (the example is in drive, so you should change the scope for email audit api in your case)
https://developers.google.com/drive/web/delegation
Make sure you complete the steps for 'Delegate domain-wide authority to your service account'.
Finally, if you take a look at the PHP code sample, you can see that you will need the scope, the user you are trying to impersonate and your service account.