struggling to understand the oauth2 token and refresh token processes
ive got this code
$url = 'https://www.googleapis.com/oauth2/v3/token';
$data = array('client_id' => 'clientid', 'client_secret' => 'secret','refresh_token' => 'token','grant_type' => 'refresh_token');
$options = array(
'http' => array(
'header' => "Content-type: application/x-www-form-urlencoded",
'method' => 'POST',
'approval_prompt'=>'force',
'access_type'=>'offline',
'content' => http_build_query($data),
),
);
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
that code above gives me an access token , and i followed this link suggested by one fellow stackoverflower, pinoyyid, BUT , im confunsed on how to correctly use the resulting access token to access drive and copy a file...
all the process ive seen usually involves $client = new Google_Client() and im not sure on how to use the whole POST http://..... thing, so basically i need to figure out if i use the access token i got with the code above in a new instance of google client, or i simply do a post to a url with necesary info ( which im not clear on also ) any help/clarification is appreciated guys really
EDIT #1
what i want to achieve is to allow the end user to access my drive via my webpage, to let them copy a spreadsheet in my drive , and access it via my website, to store data on the spreadsheet,the spreadsheet will always be on my drive, never on the end user
EDIT #2
code as per your posts is as follows, using the service account,,,,the files are inside that gmail account which i created on the api console a service account
<?php
require 'Google/autoload.php';
$client = new Google_Client();
// Replace this with your application name.
$client->setApplicationName("TEST");
// Replace this with the service you are using.
$service = new Google_Service_Drive($client);
// This file location should point to the private key file.
$key = file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/number-privatekey.p12');
$user_to_impersonate = 'admin#testpr.com';
$cred = new Google_Auth_AssertionCredentials(
'number#developer.gserviceaccount.com',
array('https://www.googleapis.com/auth/drive'), ****//this here has to be drive not drive.file
$key,
'notasecret',
'http://oauth.net/grant_type/jwt/1.0/bearer',
$user_to_impersonate
);
$client->setAssertionCredentials($cred);
if ($client->getAuth()->isAccessTokenExpired()) {
$client->getAuth()->refreshTokenWithAssertion();
}
$originFileId = "longnumber";
$copyTitle = 'copied';
$newfile = copyFile($service, $originFileId, $copyTitle);
print_r($newfile);
function copyFile($service, $originFileId, $copyTitle)
{
$copiedFile = new Google_Service_Drive_DriveFile();
$copiedFile->setTitle($copyTitle);
try {
return $service->files->copy($originFileId, $copiedFile);
} catch (Exception $e) {
print "An error occurred: " . $e->getMessage();
}
return NULL;
}
?>
so got it working just now, ty all for your time guys really and edited my post to reflect the dang thing
If you want to give the end user access to your drive, you have to give your application authority to make API calls on behalf of a user (in this case you) in your domain. For this you have to set up a service account and generate a p12 key in the Google Developers Console. You have to enter the https://www.googleapis.com/auth/drive API scope in your Admin Console as well.
Full explanation and examples can be found here: https://developers.google.com/api-client-library/php/auth/service-accounts.
To achieve this you also need the Google API's client library: https://github.com/google/google-api-php-client (also mentioned in the Google manual).
Code example to let users make API calls on behalf of one of your accounts: https://developers.google.com/api-client-library/php/guide/aaa_oauth2_service
Related
I am trying to create folder using google drive api. I am able to get file id at the end. but i am not able to my folder in google drive. it seems some permission issue?
$scopes = array(
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.appdata','https://www.googleapis.com/auth/drive.apps.readonly',
'https://www.googleapis.com/auth/drive.metadata','https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive.readonly','https://www.googleapis.com/auth/drive.photos.readonly'
);
$creds = new Google_Auth_AssertionCredentials(
$serviceAccountName,
$scopes,
file_get_contents($keyFile)
);
$client = new Google_Client();
$client->setApplicationName($appName);
$client->setClientId($clientId);
$client->setAssertionCredentials($creds);
$service = new Google_Service_Drive($client);
$fileMetadata = new Google_Service_Drive_DriveFile(array(
'name' => 'Invoices',
'permissions' => array('type'=>'anyone','role'=>'reader','allowFileDiscovery'=>1),
'mimeType' => 'application/vnd.google-apps.folder'));
$file = $service->files->create($fileMetadata, array());
printf("File ID: %s\n", $file->id);
The files that are being created belong to the API account email (something like api-service-account#yourproject.iam.gserviceaccount.com. If you go to the drive URL:
https://docs.google.com/document/d/{ID}, you will get a "Permission Denied, request access" page.
The service account email is not related to your user email.
In order to create files with your user, you need to create an OAauth token authorization.
Create the OAuth credentials following the steps "Step 1: Turn on the Drive API" on this page
Modify your script, by following the example on this page, you can have:
use Google_Client;
use Google_Service_Drive;
use Google_Service_Drive_DriveFile;
...
$file = 'root/to/your/credentials.json';
$client = new Google_Client();
$client->setScopes([
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.appdata',
'https://www.googleapis.com/auth/drive.apps.readonly',
'https://www.googleapis.com/auth/drive.metadata',
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive.readonly',
'https://www.googleapis.com/auth/drive.photos.readonly'
]);
$client->setAuthConfig($file);
$authUrl = $client->createAuthUrl();
printf("Open the following link in your browser:\n%s\n", $authUrl);
// You will need to open this url
print 'Enter verification code: ';
$authCode = trim(fgets(STDIN));
$accessToken = $client->authenticate($authCode);
file_put_contents($credentialsPath, $accessToken);
printf("Credentials saved to %s\n", $credentialsPath);
$client->setAccessToken(json_decode($accessToken, true));
$service = new Google_Service_Drive($client);
$metadata = new Google_Service_Drive_DriveFile([
'name' => 'testing drive',
'mimeType' => "application/vnd.google-apps.document"
]);
$file = $service->files->create($metadata, []);
print_r($file);
The file should be in your Drive home directory.
By the way, I suggest you try the new version google/apiclient:2.0.2 (which is still on Beta, but works okay).
Mayrop answer is partially right.
The files that are being created belong to the API account email (something like api-service-account#yourproject.iam.gserviceaccount.com.
You need to give permission to owner of that account like
$newPermission = new Google_Service_Drive_Permission();
$value="email id";
$type="user";
$role="writer";
$newPermission->setValue($value);
$newPermission->setType($type);
$newPermission->setRole($role);
try {
$res= $service->permissions->insert($fileId, $newPermission);
print_r($res);
} catch (Exception $e) {
print "An error occurred: " . $e->getMessage();
}
You will see that folder in Shared with me. You can also add in your drive manually.
You can also disable sending notification via email using
$service->permissions->insert($fileId, $newPermission,array('sendNotificationEmails'=>false));
Hope this will work for you.
Yes, It seems permissions issue. Try removing
'permissions' => array('type'=>'anyone','role'=>'reader','allowFileDiscovery'=>1),
from
$fileMetadata = new Google_Service_Drive_DriveFile(array(
'name' => 'Invoices',
'permissions' => array('type'=>'anyone','role'=>'reader','allowFileDiscovery'=>1),
'mimeType' => 'application/vnd.google-apps.folder'));
so I have worked over it, and finally got the full working solution for Google Drive V3 in Php. As follows:
You can go through this gist for full code and better understanding:
https://gist.github.com/KartikWatts/22dc9eb20980b5b4e041ddcaf04caf9e
First of all adding to the answer from #Hitu Bansal:
This code works fine:
$this->newPermission = new Google_Service_Drive_Permission();
$value=<YOUR EMAIL ADDRESS>;
$type="user";
$role="reader";
$this->newPermission->setEmailAddress($value);
$this->newPermission->setType($type);
$this->newPermission->setRole($role);
$this->newPermission->allowFileDiscovery;
try {
$res= $this->service->permissions->create($folder_id, $this->newPermission);
print_r($res);
catch (Exception $e) {
print "An error occurred: " . $e->getMessage();
}
If, instead you want to share the link with anyone: You may use
$type="anyone";
$this->newPermission->setType($type);
$this->newPermission->setRole($role);
$this->newPermission->allowFileDiscovery;
$this->newPermission->view;
NOTE:
In this case, setEmailAddress() shall be removed.
IMPORTANT: In case of 'anyone', the folder will not be visible in your personal drive for sure, but you can still visit and interact with the folder as per given roles by visiting the link making use of the folder_id that you know already. So, you can visit the link as: https://drive.google.com/drive/u/5/folders/**{folder_id}** It will be publically accessible now.
For more clear reference to the parameters along with a description, this is really helpful: https://developers.google.com/drive/api/v3/reference/permissions#methods
NOTE: In my personal opinion, a better way to work with Google Drive API is to create the folder first manually (if workflow allows it), then upload the files using the folder_id of that created folder. Remember, In case folder is created manually, permission shall be provided to the Service Account email-id before.
While permission issue is the main cause of this problem. What I did to make the folders or files appear after I uploaded it with service account was to specify the parent folder. If you upload / create folder / files without parent folder ID, that object's owner will be the service account that you are using.
By specifying parent ID, it will use the inherited permissions.
Here's the code I use in php (google/apiclient)
$driveFile = new Google\Service\Drive\DriveFile();
$driveFile->name = 'New Folder';
$driveFile->mimeType = 'application/vnd.google-apps.folder';
$driveFile->parents = ['123456789qwertyuiop'];
$result = $service->files->create($driveFile);
I had a problem with updating user cover pic using php(zend framework) and Oauth.
I have added to my composer.json the following lines:
"require" : {
"google/auth": "0.7",
"google/apiclient" : "^2.0.0#RC"
}
After that I made composer-install + composer-update using and oppp I get the library inside my vendor.
I have configured my application inside google developing console, following the official tutorial by google :D
Now inside my controller I could easily request google web service using this method :
public function googleplusAction()
{
Zend_Loader::loadFile("HttpPost.class.php");
$client_id = "id_here";
$client_secret = "secret_here";
$application_name = "application_name_here";
$redirect_uri = "redirection_uri_here";
$oauth2_server_url = 'https://accounts.google.com/o/oauth2/auth';
$query_params = array(
'response_type' => 'code',
// The app needs to use Google API in the background
'client_id' => $client_id,
'redirect_uri' => $redirect_uri,
'scope' => 'https://www.googleapis.com/auth/userinfo.profile'
);
$forward_url = $oauth2_server_url . '?' . http_build_query($query_params);
header('Location: ' . $forward_url);
}
After that I get redirected to my redirection URI , and in the bar address I get a new variable 'code'.
Until now, I hope everything is fine , coming to the most important part , the controller of the redirection URI page , using the 'code' variable that I have talked about it before I tried to get an access token, but I was failed.
This is the method that should set a new cover picture on google plus :
$client_id = "client-id";
$client_secret = "g+-secret";
$application_name = "my-app-name";
$redirect_uri = "my-uri-on-g+";
$client = new Google_Client();
$client->setClientId($client_id);
$client->setClientSecret($client_secret);
$client->setRedirectUri($redirect_uri);
$service = new Google_Service_Oauth2($client);
$client->addScope(Google_Service_Oauth2::USERINFO_PROFILE);
$client->authenticate($_GET['code']); // I have the right code, and I am being authenticated
$plus = new Google_Service_Plus($client);
$person = $plus->people->get('me');
var_dump($person);
$pic = $this->session->image['generatedAbs'];
$gimg = new Google_Service_Plus_PersonCover();
$source = new Google_Service_Plus_PersonCoverCoverPhoto();
$source ->setUrl("$photo-that-i-wanted-to-put-on-g+");
$gimg->setCoverPhoto($source);
$person->setCover($gimg);}
So my questions are :
How can I change my google plus cover picture to a new png or JPEG picture that I have already in my project ?
inside the G+ library I found this method :
Google_Service_Plus_PersonCoverCoverPhoto();
inside a class called
Google_Service_Plus_PersonCover();
But how can I use it ?
I think that methods Google_Service_Plus_PersonCoverCoverPhoto() and Google_Service_Plus_PersonCover() are used by the client library to set it when the information is retrieved. It is not meant for you to be able to update the users cover on Google+, if that does work it will only update the class object which really there is no point in doing (IMO).
var_dump($person->Cover);
If you check the Plus.People documentation you will notice there are no update or patch methods. This is because its not possible to update a users information programmatically at this time.
Answer: Your inability to update the users cover picture has nothing to do with Oauth it has to do with the fact that this is not allowed by the API. Unfortunately it looks like you have done a lot of work for nothing this is why it is good to always consult the documentation before you begin you would have seen that it was not possible, and could have avoided a lot of unnecessary stress on yourself.
Using the code below
define('DRIVE_SCOPE', 'https://www.googleapis.com/auth/drive');
define('SERVICE_ACCOUNT_EMAIL', 'xxx.iam.gserviceaccount.com');
define('SERVICE_ACCOUNT_PKCS12_FILE_PATH', 'xxx');
function buildService($userEmail) {
$key = file_get_contents(SERVICE_ACCOUNT_PKCS12_FILE_PATH);
$auth = new Google_Auth_AssertionCredentials(
SERVICE_ACCOUNT_EMAIL,
array(DRIVE_SCOPE),
$key);
//$auth->sub = $userEmail;
$client = new Google_Client();
$client->setAssertionCredentials($auth);
return new Google_Service_Drive($client);
}
$service = buildService('xxx.apps.googleusercontent.com');
$fileMetadata = new Google_Service_Drive_DriveFile(array(
'name' => 'Invoices',
'mimeType' => 'application/vnd.google-apps.folder'
));
$file = $service->files->create($fileMetadata, array(
'fields' => 'id'
));
printf("Folder ID: %s\n", $file->id);
I have been trying to create a folder on my drive account through PHP. I believe everything is configured correctly and I am getting a result
Folder ID: 0B2nllBpB_k0NQWFNUjlSc0NUdE0
But the folder is not being created on my drive account. The same goes with creating files as well. It keeps returning an ID but nothing is actually created. What exactly am I doing wrong?
Your current code is authenticating as the service account and creating a file. The resultant file would then only be accessible to the service account, not your Google user. You can confirm this by doing a list of files instead of a create in your code, you'll get back the files you've previously created.
Your code has sub= commented out. Uncommented that line and make sure you've followed the domain-wide delegation instructions so that your code can authenticate and act as your Google user.
I was wondering is there any method to upload the files directly to our own Google drive by using php , Currently i am using a method like this.
$drive = new Google_Client();
$drive->setClientId($client_id);
$drive->setClientSecret($client_secret);
$drive->setRedirectUri($redirect_uri);
$drive->setScopes(array('https://www.googleapis.com/auth/drive'));
$gdrive = new Google_DriveService($drive);
$_GET['code'] = 'ya2-------------------------OZXiA';
//file_put_contents('token.json', $drive->authenticate());
$drive->setAccessToken(file_get_contents('token.json'));
$doc = new Google_DriveFile();
$doc->setTitle('Test Document');
$doc->setDescription('Test description');
$doc->setMimeType('text/plain');
$content = file_get_contents('/assets/new--detils.txt');
$output = $gdrive->files->insert($doc, array(
'data' => $content,
'mimeType' => 'text/plain',
));
print_r($output);
But this shows an error as
Fatal error: Uncaught exception Google_AuthException with message 'Error fetching OAuth2 access token, message: 'invalid_grant'' in /var/www/path/src/auth/Google_OAuth2.php:115
Here i am using $_GET['code'] = 'ya2-------------------------OZXiA' generated for that app,
Can anyone please suggest a method to do this, thanks in advance.
Not sure if it helps but I found this looking through Google Code...
"You'll get the invalid_grant error when you try to use the same authorization code."
I found it here: https://code.google.com/p/google-api-php-client/issues/detail?id=94
Not sure if that will help you or not...
Also found this tutorial: http://25labs.com/tutorial-implementing-google-api-using-oauth-2-0-in-php/
$drive = new Google_Client();
$drive->setClientId($client_id);
$drive->setClientSecret($client_secret);
$drive->setRedirectUri($redirect_uri);
**$drive->setAccessType('offline');**
$authUrl = $client->createAuthUrl();
echo $authUrl;
Now go the auth url and get the refresh token and other things.
Make sure this is the first time you are asking for permission, if you aren't then you wont get the refreshtoken which is necessary to make authtokens in the future.
If you have already give permissions just go to your google account and revoke the permissions of the app.
This is what you should have in the file which you are redirecting too.
$tokeninfo = $drive->getAccessToken();
print_r($tokeninfo);
Now save the tokeninfo in a file like "token.json".
Now go back to the original file which will contain the code for upload.
$drive = new Google_Client();
$drive->setClientId($client_id);
$drive->setClientSecret($client_secret);
$drive->setRedirectUri($redirect_uri);
$drive->setScopes(array('https://www.googleapis.com/auth/drive'));
$drive->setAccessType('offline');
$service = new Google_Service_Drive($drive);
$refreshToken = "Your refresh token";
/* get it from token.json and it will look some like this 1**\/**asasfasfsfsd
remove the \ since this is actually json encoded.*/
$tokens = file_get_contents(TOKEN); /*TOKEN = the token.json file*/
$drive->setAccessToken($tokens);
if ($drive->isAccessTokenExpired()) {
$drive->refreshToken($refreshToken);
file_put_contents(TOKEN,$drive->getAccessToken());
}
Now you have done the authentication and just need to upload the file.
if ($drive->getAccessToken()) {
$file = new Google_Service_Drive_DriveFile();
$file->title ="the file name";
/*the upload code can be found in the examples*/
}
Note
The file you upload cannot be downloaded by other ppl, you need to set googlepermissions to make the file shared.
Just drop a comment if you need to code to add permissions.
i am using php 5.3.3, and codeigniter 2.1.0.
what i want to do is set up a service account, so a user can add an appointment in a text entry field on my website, then have that appointment added to a shared shared google calendar.
i have a google account, and using : https://code.google.com/apis/console I created a new project called 'pqp'
on services: enabled the calendar api
on api access: i created an oath 2.0 client id… product name = pqp, application type = service account.
downloaded the key 46… -privatekey.p12. there is a screenshot of the settings:
I got an svn checkout of the google-api-php-client (28/6/2012)
In the google-api-php-client/src/config.php I changed lines:
25: 'application_name' => 'pqp',
28: 'oauth2_client_id' => '373xxx730.apps.googleusercontent.com',
57: 'ioFileCache_directory' => 'tmp/apiClient', // my apache user does not have access to the system /tmp folder. + tmp/apiClient has permissions of 777 on the server.
using this link:
http://code.google.com/p/google-api-php-client/source/browse/trunk/examples/prediction/serviceAccount.php?spec=svn445&r=395
I modified it to:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Test extends CI_Controller{
function __construct()
{
parent::__construct();
}
function index()
{
set_include_path(get_include_path() . PATH_SEPARATOR .dirname(__FILE__).'/../libraries/google-api-php-client/src');
ini_set('error_reporting',E_ALL);
ini_set('display_errors','1');
// Set your client id, service account name, and the path to your private key.
// For more information about obtaining these keys, visit:
// https://developers.google.com/console/help/#service_accounts
define('CLIENT_ID','3731xxx44730.apps.googleusercontent.com');
define('SERVICE_ACCOUNT_NAME','373xxx244730#developer.gserviceaccount.com');
// Make sure you keep your key.p12 file in a secure location, and isn't
// readable by others.
define('KEY_FILE',dirname(__FILE__).'/../../461290xxx796c0b7db9582c-privatekey.p12');
require_once "apiClient.php";
require_once "contrib/apiCalendarService.php";
$client = new apiClient();
$client->setApplicationName("pqp");
// Set your cached access token. Remember to replace $_SESSION with a
// real database or memcached.
session_start();
if (isset($_SESSION['token'])) {
$client->setAccessToken($_SESSION['token']);
echo 'client access token is set.<br/>';
}
// Load the key in PKCS 12 format (you need to download this from the
// Google API Console when the service account was created.
$key = file_get_contents(KEY_FILE);
$creds = new apiAssertionCredentials(SERVICE_ACCOUNT_NAME,array('https://www.googleapis.com/auth/calendar'),$key);
$client->setAssertionCredentials($creds);
$client->setClientId(CLIENT_ID);
$service = new apiCalendarService($client);
echo 'client:<br/>';
var_dump($client);
echo 'service:<br/>';
var_dump($service);
// We're not done yet. Remember to update the cached access token.
// Remember to replace $_SESSION with a real database or memcached.
if ($client->getAccessToken()) {
$_SESSION['token'] = $client->getAccessToken();
echo 'token is good!, so creating an event....';
echo $this->insert_event($service,'testing summary','my location','2012-06-29T10:00:00.000+10:00','2012-06-29T10:00:00.000+10:00');
}
}
function insert_event($service,$summary,$location,$from,$to){
$event = new Event();
$event->setSummary($summary);
$event->setLocation($location);
$start = new EventDateTime();
$start->setDateTime($from);
$event->setStart($start);
$end = new EventDateTime();
$end->setDateTime($to);
$event->setEnd($end);
$attendee1 = new EventAttendee();
$attendee1->setEmail('test#example.com');
$attendees = array($attendee1);
$event->attendees = $attendees;
$createdEvent = $service->events->insert('primary', $event);
return $createdEvent->getId();
}
}
a pastie of the output is here:
the $client object is not authenticated, the getAccessToken is not set, and the event is not inserted.
i have found it difficult to work out which settings in the $config file to change because there is different nomenclature. i guess this is an artifact of how the code has progressed.
are the settings in src/config.php correct? do i need to alter any more settings?
it is my understanding that if i create the service account, download the key file, and the contents of this file with my developer id, it should return a token, and there is no need to set up a redirection uri.. is that correct? this is the functionality i want. i don't want the user to have to authorise access because the website will only ever interact with one google account.
So, the question is, how do i get this calendar api to authenticate using a google service account?
You can try this one if you not already...
EDIT: this one intresting but if you still feel like to write one yourself then try this THE mother of the oauth2. helpful
About Service Account at google-api-php-client that you use(always take the trunk's one with SVN) I can't found in that code manipulations any reference apiAssertionCredentials::generateAssertion()
definitely there is no call to auth in there
public function __construct(
$serviceAccountName,
$scopes,
$privateKey,
$privateKeyPassword = 'notasecret',
$assertionType = 'http://oauth.net/grant_type/jwt/1.0/bearer',
$prn = false) {
$this->serviceAccountName = $serviceAccountName;
$this->scopes = is_string($scopes) ? $scopes : implode(' ', $scopes);
$this->privateKey = $privateKey;
$this->privateKeyPassword = $privateKeyPassword;
$this->assertionType = $assertionType;
$this->prn = $prn;
}
and this method should be called I guess...
public function generateAssertion() {
$now = time();
$jwtParams = array(
'aud' => apiOAuth2::OAUTH2_TOKEN_URI,
'scope' => $this->scopes,
'iat' => $now,
'exp' => $now + self::MAX_TOKEN_LIFETIME_SECS,
'iss' => $this->serviceAccountName,
);
if ($this->prn !== false) {
$jwtParams['prn'] = $this->prn;
}
return $this->makeSignedJwt($jwtParams);
}
EDIT:
Pardon. In fresh ver. look's like it's Google_OAuth2::refreshTokenWithAssertion() actualy who should start the real proces