I am trying to use a single Google Drive account as a \'web server.\' I have an Android app that needs to be able to store and retrieve pictures. My idea was to use Parse to
OK. So after hours of research and testing, I have come up with a way of getting this to work.
Google's API authentication process is a bit confusing to say the least. The general procedure is as follows:
INSERT YOUR PROJECT HERE
for the scope YOUR SCOPE
So the whole goal of this is to make API calls (specifically to drive) which requires a refresh code.
The main difficulty in this is, (AS FAR AS I KNOW) there is no way of authenticating a user and getting an access code inside of Parse CloudCode. BUT, I had a theory that if I was somehow able to authenticate the user account outside of CloudCode, you could still make GET
/POST
requests from inside a CloudCode function. (More specifically: Parse.Cloud.httpRequest
)
There are many ways/platforms that allow you to authenticate a user in order to get a refresh code. The most accessible method would probably be by using the Android/Java version of the API because anyone with a computer can run an Android emulator, but I have easy access to a PHP capable website, so I chose to use PHP instead. But again, this part of the process can be done on many different platforms.
The first step is to install the Google API Client Library for PHP (Github). There are a few different ways to install this on your server, but since I only need it to get a refresh token, I chose to include it dynamically at runtime (also because I didn't have quick access to my php.ini
file).
(Note before I continue: I am not a PHP developer, so please let me know if I do anything odd or redundant; I am merely showing what I did to get this to work)
To do this I simply downloaded a copy of the library, uploaded it to my server, and included the following line in all the files I used the library:
set_include_path(get_include_path() . PATH_SEPARATOR . 'google-api-php-client-master/src');
Where google-api-php-client-master/src
is the path to your src folder.
Once you have that done, you have to authenticate the user. The following two files send the user to an authentication page, and then print the refresh code for that user on the screen.
index.php
:
<?php
set_include_path(get_include_path() . PATH_SEPARATOR . 'google-api-php-client-master/src');
require_once 'google-api-php-client-master/src/Google/autoload.php'; // or wherever autoload.php is located
session_start();
$client = new Google_Client();
$client->setAuthConfigFile('client_secrets.json'); //You can download this file from your developer console under OAuth 2.0 client IDs
$client->addScope(Google_Service_Drive::DRIVE); //Scope that grants full access to user's Google Drive
$client->setAccessType("offline"); //Important so a refresh code is returned
if (isset($_SESSION['access_token']) && $_SESSION['access_token']) {
print($_SESSION['refresh_token']);
} else {
$redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php';
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
?>
oauth2callback.php
:
<?php
set_include_path(get_include_path() . PATH_SEPARATOR . 'google-api-php-client-master/src');
require_once 'google-api-php-client-master/src/Google/autoload.php';
session_start();
$client = new Google_Client();
$client->setAuthConfigFile('client_secrets.json');
$client->setRedirectUri('http://' . $_SERVER['HTTP_HOST'] . '/oauth2callback.php');
$client->addScope(Google_Service_Drive::DRIVE);
$client->setAccessType("offline");
if (! isset($_GET['code'])) {
$auth_url = $client->createAuthUrl();
header('Location: ' . filter_var($auth_url, FILTER_SANITIZE_URL));
} else {
$client->authenticate($_GET['code']);
$_SESSION['access_token'] = $client->getAccessToken();
$_SESSION['refresh_token'] = $client->getRefreshToken(); //Important to clear the session variable after you have the token saved somewhere
$redirect_uri = 'http://' . $_SERVER['HTTP_HOST'] . '/'; //Redirects to index.php
header('Location: ' . filter_var($redirect_uri, FILTER_SANITIZE_URL));
}
?>
Several important notes about this code:
http://yourdomain.com/oauth2callback.php
.client_secrets.json
file to your server. You can get this by going to your developers console and clicking the download icon on the far right side of the OAuth 2.0 client IDs list.So in summary, we have created a simple program that prints out the refresh token for a specific user. You need to copy that code down and save it for later.
Even though you have the refresh token, you need a way of getting an access token to make API calls. (You can't make an API call without an access token, and because they expire every 3600 seconds, you need a way of getting a new one when you need it)
Here is the Parse CloudCode for accomplishing this step:
Parse.Cloud.define("getAccessToken", function(request, response) {
Parse.Cloud.httpRequest({
method: "POST",
url: 'https://www.googleapis.com/oauth2/v3/token/',
params: {
refresh_token : 'INSERT_REFRESH_TOKEN_HERE',
client_id : 'INSERT_CLIENT_ID_HERE',
client_secret : 'INSERT_CLIENT_SECRET_HERE',
grant_type : 'refresh_token'
}
}).then(function(httpResponse) {
response.success(httpResponse.text);
}, function(httpResponse) {
response.error('Request failed with response code ' + httpResponse.status);
});
});
This function sends a request for an access token. You can do whatever you want with that token; it is stored in httpResponse.text
(if the request was successful). Note that grant_type
should be refresh_token
not YOUR refresh token.
Once you have an access token, you are free to make API calls within the scope of your refresh token. Example:
Parse.Cloud.define("sendRequest", function(request, response) {
Parse.Cloud.httpRequest({
method: "GET",
url: 'https://www.googleapis.com/drive/v2/about',
params: {
access_token : 'INSERT_YOUR_ACCESS_TOKEN_HERE'
}
}).then(function(httpResponse) {
response.success(httpResponse.text);
}, function(httpResponse) {
response.error('Request failed with response code ' + httpResponse.status);
});
});
This function returns information about the drive account associated with that refresh token.
With regards to my second question, I have not done enough research to know the answer to that yet, but I plan on finding out and posting it here.