I have a discord bot, written in node.js using discord.js, that accesses a slice of my Google Drive for storing user preferences and data. Everything is currently working. It only accesses my Google account.
So far, I don't understand what to do in response to the email I received from Google last night, subject "[Action Required] Migrate your OAuth out-of-band flow to an alternative method before Oct. 3, 2022".
My bot currently uses a slightly modified version of the node.js quickstart code, found here: https://developers.google.com/drive/api/quickstart/nodejs (the modifications check for environment variables for authorization data, before checking for that data in files).
The bot is hosted at Heroku as a worker dyno (not a web dyno) and frankly it doesn't seem sensible that I should need to add web server functionality to the bot just to log in to Google... I'm probably missing something reasonably easy in their gobbledygook blog post and help files.
Relevant blog post: https://developers.googleblog.com/2022/02/making-oauth-flows-safer.html
Relevant help file: https://developers.google.com/identity/protocols/oauth2/native-app#redirect-uri_loopback
The relevant part of the bot's code:
// @ ============ GOOGLE * google * Google ===========
const fs = require('fs');
const readline = require('readline');
const {google} = require('googleapis');
// If modifying these scopes, delete googletoken.json.
const G_SCOPES = ['https://www.googleapis.com/auth/drive.appdata',
'https://www.googleapis.com/auth/drive.file'];
// The file googletoken.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
const G_TOKEN_PATH = 'googletoken.json';
// Load client secrets from env or local file.
if (process.env.hasOwnProperty('GOOGLE_CREDENTIALS')) {
authorize(JSON.parse(process.env.GOOGLE_CREDENTIALS), initAll);
} else {
fs.readFile('googlecredentials.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), initAll);
});
}
// @ =========== google's library functions =============
/**
* 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.
if (process.env.hasOwnProperty('GOOGLE_TOKEN')) {
oAuth2Client.setCredentials(JSON.parse(process.env.GOOGLE_TOKEN));
callback(oAuth2Client);
} else {
fs.readFile(G_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: G_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(G_TOKEN_PATH, JSON.stringify(token), (err) => {
if (err) return console.error(err);
console.log('Token stored to', G_TOKEN_PATH);
});
callback(oAuth2Client);
});
});
}