Authorization of Google Drive using JavaScript
It is possible to use the Google APIs Javascript client library with Drive but you have to be aware that there are some pain points.
There are 2 main issues currently, both of which have workarrounds:
Authorization
First if you have a look closely at how Google Drive auth works you will realize that, after a user has installed your Drive application and tries to open a file or create a new file with your application, Drive initiates the OAuth 2.0 authorization flow automatically and the auth parameters are set to response_type=code and access_type=offline.
This basically means that right now Drive apps are forced to use the OAuth 2 server-side flow which is not going to be of any use to the Javascript client library (which only uses the client-side flow).
The issue is that: Drive initiates a server-side OAuth 2.0 flow, then the Javascript client library initiates a client-side OAuth 2.0 flow.
This can still work, all you have to do it is use server-side code to process the authorization code returned after the Drive server-side flow (you need to exchange it for an access token and a refresh token).
That way, only on the first flow will the user be prompted for authorization. After the first time you exchange the authorization code, the auth page will be bypassed automatically.
Server side samples to do this is available in our documentation.
If you don't process/exchange the auth code on the server-side flow, the user will be prompted for auth every single time he tries to use your app from Drive.
Handling file content
The second issue is that uploading and accessing the actual Drive file content is not made easy by our Javascript client library. You can still do it but you will have to use custom Javascript code.
Reading the file content
When a file metadata/a file object is retrieved, it contains a downloadUrl
attribute which points to the actual file content. It is now possible to download the file using a CORS request and the simplest way to auth is to use the OAuth 2 access token in a URL param. So just append &access_token=...
to the downloadUrl
and fetch the file using XHR or by forwarding the user to the URL.
Uploading file content
UPDATE UPDATE: The upload endpoints do now support CORS.
~~UPDATE: The upload endpoints, unlike the rest of the Drive API do not support CORS so you'll have to use the trick below for now:~~
Uploading a file is tricky because it's not built-in the Javascript client lib and you can't entirely do it with HTTP as described in this response because we don't allow cross-domain requests on these API endpoints. So you do have to take advantage of the iframe proxy used by our Javascript client library and use it to send a constructed multipart request to the Drive SDK. Thanks to @Alain, we have an sample of how to do that below:
/**
* Insert new file.
*
* @param {File} fileData File object to read data from.
* @param {Function} callback Callback function to call when the request is complete.
*/
function insertFileData(fileData, callback) {
const boundary = '-------314159265358979323846';
const delimiter = "\r\n--" + boundary + "\r\n";
const close_delim = "\r\n--" + boundary + "--";
var reader = new FileReader();
reader.readAsBinaryString(fileData);
reader.onload = function(e) {
var contentType = fileData.type || 'application/octet-stream';
var metadata = {
'title': fileData.fileName,
'mimeType': contentType
};
var base64Data = btoa(reader.result);
var multipartRequestBody =
delimiter +
'Content-Type: application/json\r\n\r\n' +
JSON.stringify(metadata) +
delimiter +
'Content-Type: ' + contentType + '\r\n' +
'Content-Transfer-Encoding: base64\r\n' +
'\r\n' +
base64Data +
close_delim;
var request = gapi.client.request({
'path': '/upload/drive/v2/files',
'method': 'POST',
'params': {'uploadType': 'multipart'},
'headers': {
'Content-Type': 'multipart/mixed; boundary="' + boundary + '"'
},
'body': multipartRequestBody});
if (!callback) {
callback = function(file) {
console.log(file)
};
}
request.execute(callback);
}
}
To improve all this, in the future we might:
- Let developers choose which OAuth 2.0 flow they want to use (server-side or client-side) or let the developer handle the OAuth flow entirely.
- Allow CORS on the
/upload/...
endpoints - Allow CORS on the
exportLinks
for native gDocs - We should make it easier to upload files using our Javascript client library.
Allow users to upload files to a Google Drive service account
In this code TestGoogleDrive.json - your service account key.
var { google } = require('googleapis');
var key = require('./TestGoogleDrive.json');
var jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
[
'https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.file'
],
null
);
var drive = google.drive( 'v3');
jwtClient.authorize(function (err, tokens) {
if (err) {
console.log(err);
return;
}
// Make an authorized request to list Drive files.
drive.files.list({
auth: jwtClient,
pageSize: 10,
fields: "nextPageToken, files(id, name)"
}, function (err, resp) {
resp.data.files.map(file => console.log(file));
});
});
Full information you can find following link. Setting up Google Drive API on NodeJS using a service account
When I saw the showing keys of your credentials.json
file, I understood that the file is the credential file of the service account. If my understanding is correct, when I saw your showing script, it seems that the script is for OAuth2. In this case, this script cannot be used for the service account. I thought that this is the reason for your current issue.
In order to use Drive API using the service account, how about the following sample script?
Sample script:
Before you use this script, please setcredentialFilename
of the service account. In this case, please include the path.const { google } = require("googleapis");
const credentialFilename = "credentials.json";
const scopes = ["https://www.googleapis.com/auth/drive.metadata.readonly"];
const auth = new google.auth.GoogleAuth({keyFile: credentialFilename, scopes: scopes});
const drive = google.drive({ version: "v3", auth });
// This is a simple sample script for retrieving the file list.
drive.files.list(
{
pageSize: 10,
fields: "nextPageToken, files(id, name)",
},
(err, res) => {
if (err) return console.log("The API returned an error: " + err);
const files = res.data.files;
console.log(files);
}
);
- When this script is run, as a sample script, the file list is retrieved from the Google Drive of the service account. So, please modify this for your actual situation.
- This sample script uses
https://www.googleapis.com/auth/drive.metadata.readonly
as the scope. Please modify this for your actual situation.
Reference:
- Google APIs Node.js Client
Related Topics
Rendering to Js with Jinja Produces Invalid Number Rather Than String
How to Use Promise in Foreach Loop of Array to Populate an Object
Razor MVC Populating JavaScript Array with Model Array
How to Handle Oncut, Oncopy, and Onpaste in Jquery
Attaching Click Event to a Jquery Object Not Yet Added to the Dom
Regular Expression for Ip Address Validation
What Is the Point of Void Operator in JavaScript
Javascript: Dynamically Creating Variables for Loops
How to Detect When a Tab Is Focused or Not in Chrome with JavaScript
JavaScript Add Method to Object
Write Elements into a Child Iframe Using JavaScript or Jquery
Pass Variables to JavaScript in Expressjs
Get Visitors Language & Country Code with JavaScript (Client-Side)