Reputation: 2868
I've successfully used the oAuth2Client with Node using Google's Quickstart tutorial:
const fs = require('fs');
const readline = require('readline');
const {google} = require('googleapis');
// If modifying these scopes, delete token.json.
const SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly'];
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
const TOKEN_PATH = 'token.json';
// Load client secrets from a local file.
fs.readFile('credentials.json', (err, content) => {
if (err) return console.log('Error loading client secret file:', err);
// Authorize a client with credentials, then call the Google Drive API.
authorize(JSON.parse(content), listFiles);
});
/**
* Create an OAuth2 client with the given credentials, and then execute the
* given callback function.
* @param {Object} credentials The authorization client credentials.
* @param {function} callback The callback to call with the authorized client.
*/
function authorize(credentials, callback) {
const {client_secret, client_id, redirect_uris} = credentials.installed;
const oAuth2Client = new google.auth.OAuth2(
client_id, client_secret, redirect_uris[0]);
// Check if we have previously stored a token.
fs.readFile(TOKEN_PATH, (err, token) => {
if (err) return getAccessToken(oAuth2Client, callback);
oAuth2Client.setCredentials(JSON.parse(token));
callback(oAuth2Client);
});
}
/**
* Get and store new token after prompting for user authorization, and then
* execute the given callback with the authorized OAuth2 client.
* @param {google.auth.OAuth2} oAuth2Client The OAuth2 client to get token for.
* @param {getEventsCallback} callback The callback for the authorized client.
*/
function getAccessToken(oAuth2Client, callback) {
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES,
});
console.log('Authorize this app by visiting this url:', authUrl);
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question('Enter the code from that page here: ', (code) => {
rl.close();
oAuth2Client.getToken(code, (err, token) => {
if (err) return console.error('Error retrieving access token', err);
oAuth2Client.setCredentials(token);
// Store the token to disk for later program executions
fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
if (err) return console.error(err);
console.log('Token stored to', TOKEN_PATH);
});
callback(oAuth2Client);
});
});
}
/**
* Lists the names and IDs of up to 10 files.
* @param {google.auth.OAuth2} auth An authorized OAuth2 client.
*/
function listFiles(auth) {
const drive = google.drive({version: 'v3', auth});
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;
if (files.length) {
console.log('Files:');
files.map((file) => {
console.log(`${file.name} (${file.id})`);
});
} else {
console.log('No files found.');
}
});
}
However, I want to avoid the consent screen and do a service to service authentication.
I've found Node GToken to get a token, but where do I place it in the code, and how do I use it?
const key = '-----BEGIN RSA PRIVATE KEY-----\nXXXXXXXXXXX...';
const { GoogleToken } = require('gtoken');
const gtoken = new GoogleToken({
email: '[email protected]',
scope: ['https://scope1', 'https://scope2'], // or space-delimited string of scopes
key: key
});
I've also found the following code on how to implement Service to Service Authentication, but it seems to be implemented for Google's compute service. The example states:
For example, a JWT auth client will be created when your code is running on your local developer machine, and a Compute client will be created when the same code is running on a configured instance of Google Compute Engine.
const {google} = require('googleapis');
const compute = google.compute('v1');
async function main () {
// This method looks for the GCLOUD_PROJECT and GOOGLE_APPLICATION_CREDENTIALS
// environment variables.
const auth = await google.auth.getClient({
// Scopes can be specified either as an array or as a single, space-delimited string.
scopes: ['https://www.googleapis.com/auth/compute']
});
// obtain the current project Id
const project = await google.auth.getProjectId();
// Fetch the list of GCE zones within a project.
const res = await compute.zones.list({ project, auth });
console.log(res.data);
}
main().catch(console.error);
I've found code on other sites that uses googleapis.auth.JWT
, but I can't find any documentation on that for service to service accounts used with Google Apps.
Here it says that a service account isn't what's needed. Unfortunately, I don't understand what the question is to understand if the answer applies to my situation, too. What I want to do is to authorize my node.js app to use my drive without the need for me to allow it to access every time.
I don't know how to translate the Quickstart code to accommodate a service account.
Upvotes: 3
Views: 1858
Reputation: 66
I did the following to send emails from Gmail API with a Service Account. Gmail API has to be enabled in Google Cloud. This is useful to send emails from an specific gmail address you own to others, without any auth screen.
const { google } = require('googleapis');
const { config } = require('dotenv');
const fs = require('fs');
config();
const SCOPES = ['https://www.googleapis.com/auth/gmail.readonly', 'https://www.googleapis.com/auth/gmail.modify'];
async function main() {
const privatekey = JSON.parse(fs.readFileSync(process.env.GOOGLE_APPLICATION_CREDENTIALS));
console.log(privatekey.client_email)
const auth = new google.auth.JWT({
subject: 'THE_EMAIL_ADDRESS_YOU_OWN',
email: privatekey.client_email,
key: privatekey.private_key,
scopes: SCOPES
});
const gmail = google.gmail({ version: 'v1', auth: auth });
const destinatary = '[email protected]'
const message = `To: ${destinatary}\r\n` +
'Subject: Mail Subject \r\n' +
'\r\n' +
'Mail Content';
const response = await gmail.users.messages.send({
userId: 'me',
requestBody: {
raw: Buffer.from(message).toString('base64'),
}
});
console.log('Mail Sent:', response.data);
}
main()
The .env file has to look like this, just containing the path to your service account key.
GOOGLE_APPLICATION_CREDENTIALS=./gmail_sa.json
Upvotes: 0
Reputation: 11692
I don't have access to a "Google Drive for Business" account, so I'm posting what I could do with my personal Google Drive ("My Drive").
I also read on some pages on Internet that you can't share a "Team Drive", but maybe you could add the service account as a team member.
This code lists the files in a folder on "My Drive" after I shared the folder with a service account.
// load library
const { google } = require('googleapis');
// configure a JWT auth client
const privatekey = require("./privatekey.json");
const jwtClient = new google.auth.JWT(
privatekey.client_email,
null,
privatekey.private_key,
[
'https://www.googleapis.com/auth/drive.readonly',
'https://www.googleapis.com/auth/drive.metadata.readonly'
]
);
jwtClient.authorize()
.then(function (tokens) {
// console.log(tokens);
console.log("Authentication successfull.\n");
// list the files once authenticated
listFiles(jwtClient);
})
.catch(function (error) {
throw (error);
});
function listFiles(auth) {
const drive = google.drive({ version: 'v3', auth });
drive.files.list({
pageSize: 10,
fields: 'nextPageToken, files(id, name)',
// q: "name contains 'foo'"
})
.then(res => {
console.log(res.data);
})
.catch(err => console.log(err));
}
privatekey.json
is the private key of the service account (downloaded and renamed) from "Google Cloud Platform", where of course "Google Drive API" is enabled.
Inside privatekey.json
you can find the email address for the service account which you could then try to add it to the "Team Drive".
Upvotes: 4