Using Google Drive API + Parse to store files on a single Drive account

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 help manage everything and extend my storage capacity beyond Parse's free amount.

Essentially, I will have a single Google Drive account and a Parse project. When a user wants to store a file, he/she uploads the file to Parse, Parse authenticates with the single Google Drive account (using CloudCode), Parse uploads the files to Drive, stores the URL to the file in a table, and deletes the file from Parse's cloud storage. I plan on giving the folder that stores these files private write access and public read access so the clients don't have to make Parse requests to

The purpose of this is to get more storage for my application. (Amazon S3 only gives 5g, Parse gives 1g, DropBox 2g, Google Cloud Storage I don't think they have a free plan, and Drive gives 15g but I have also heard about Google Photos integrating with Google Drive which might give me unlimited storage for pictures)

Because Google Drive wasn't really designed to do this, I am having some difficultly figuring out how all the pieces fit together.

I have looked at this question which doesn't seem to apply to my situation because I will be able to run all my write operations on a secure sever. (Also I have read I need to store a refresh token which should also be secure using this method)

I have looked at this but many of the links are outdated, so that didn't help me much.

I have looked at this, but this seems to be authorizing an app's use of the user's personal files.

I have also read that I may need to use a service account instead if a Web Application account, but again, the information I have been reading has not been very clear. In my opinion, this is because Drive was not designed to work this way.

Summary:

1:

Can someone point me in the right direction for writing files to a single Google Drive account using Parse Cloud Code (Server-side Javascript)?

2:

If the above question is solvable/possible, does Google's release of Google Photo's mean that I would have essentially unlimited storage capacity for pictures?

Answers:

Answer

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:

  1. User is asked to grant access to INSERT YOUR PROJECT HERE for the scope YOUR SCOPE
  2. The user can either accept or reject that request
  3. If the user accepts, you can retrieve an access token (and more importantly a refresh token)
  4. Once you have the refresh token you can get a new access token to make API calls any time you want.

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:

  1. You need to make sure you have created the appropriate credentials (Web application)
  2. You need to add the correct redirect URI to your OAuth 2.0 client ID. It should be something like http://yourdomain.com/oauth2callback.php.
  3. You need to have uploaded your 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.
  4. You should probably clear your session variables after you copy the refresh token down for security reasons.

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.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.