google authorize | | use selenium to authorize Google access | Search

This JavaScript module handles OAuth2 authentication with Google Calendar using various external libraries and provides error handling and fallback scenarios to recover from connection issues. The module defines four primary functions: storing tokens, exchanging codes for OAuth2 tokens, error fallback, and renewing existing tokens, with the last function having incomplete documentation and implementation.

Run example

npm run import -- "google oauth token client"

google oauth token client

var readline = require('readline');
var process = require('process');
var path = require('path');
var fs = require('fs');
var util = require('util');
var {OAuth2Client} = require('google-auth-library');

var importer = require('../Core');
//var runSeleniumCell = importer.import("selenium cell");

// Load client secrets from a local file.
var SCOPES = ['https://www.googleapis.com/auth/calendar.readonly'];
var SECRET_PATH, credentials;
var TOKEN_DIR = path.join(process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE || '', '.credentials');

try {
    SECRET_PATH = path.join(TOKEN_DIR, 'client_secret.json');
    credentials = JSON.parse(fs.readFileSync(SECRET_PATH).toString());
} catch(e) {
    console.log(e);
}

function storeToken(token, tokenPath) {
    fs.writeFileSync(tokenPath, JSON.stringify(token, null, 4));
    console.log('Token stored to ' + tokenPath);
}

function receiveCode(code, oauth2Client, tokenPath) {
    return util.promisify(oauth2Client.getToken.bind(oauth2Client))(code)
        .then(token => {
            console.log('recieved token: ' + token.access_token);
            oauth2Client.setCredentials(token);
            storeToken(token, tokenPath);
            return oauth2Client;
        })
}

async function errorFallback(up, authUrl, oauth2Client, tokenPath) {
    if(up.message.includes('ECONNREFUSED')
      || up.message.includes('find module')
      || up.message.includes('runSeleniumCell is not defined')) {
        console.log('can\'t authenticate with selenium, waiting for user input.');
        console.log('Authorize this app by visiting this url:', authUrl);
        //if (process.stdout.isTTY) {
        //}
        var interface = readline.createInterface(process.stdin, process.stdout);
        var code = await new Promise(resolve => interface.question(
            'Enter the code from that page here: ', resolve))
        interface.close();
        return receiveCode(code, oauth2Client, tokenPath)
    } else {
        throw up;
    }
}

async function renewToken(oauth2Client, scopes, tokenPath) {
    var authUrl = oauth2Client.generateAuthUrl({
        access_type: 'offline',
        scope: scopes
    })
    try {
        var {authorizeSelenium} = await runSeleniumCell(['log in Google', 'selenium authorize google access'])
        console.log('using selenium to authorize: ' + authUrl)
        var code = await authorizeSelenium(authUrl) 
        return receiveCode(code, oauth2Client, tokenPath)
    } catch (up) {
        return errorFallback(up, authUrl, oauth2Client, tokenPath)
    }
}

async function authorize(scopes = SCOPES) {
    const tokenPath = path.join(TOKEN_DIR, scopes.join('')
        .replace(/[^a-z]+/ig, '_') + '.json')
    var oauth2Client = new OAuth2Client(
        credentials.web.client_id,
        credentials.web.client_secret,
        credentials.web.redirect_uris[0])

    try {
        // Check if we have previously stored a token.
        oauth2Client.setCredentials(JSON.parse(fs.readFileSync(tokenPath)));
        await oauth2Client.getAccessToken()
        return oauth2Client
    } catch (up) {
        // if the token file isn't found start a new auth
        if(up.message == 'invalid_token'
          || up.code === 'ENOENT') {
            return renewToken(oauth2Client, scopes, tokenPath);
        } else {
            throw up;
        }
    }
}

module.exports = authorize;

What the code could have been:

const readline = require('readline');
const { OAuth2Client } = require('google-auth-library');
const fs = require('fs').promises;
const path = require('path');
const { sep } = require('path');
const util = require('util');
const console = console;

const importer = require('../Core');
const { runSeleniumCell } = importer.import('selenium cell');

const SCOPES = ['https://www.googleapis.com/auth/calendar.readonly'];
const SECRET_PATH = path.join(path.dirname(require.main.filename), 'client_secret.json');
const TOKEN_DIR = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE || '', '.credentials';

async function authorize(scopes = SCOPES) {
    const tokenPath = path.join(TOKEN_DIR, scopes.join('')
       .replace(/[^a-z]+/ig, '_') + '.json');
    const oauth2Client = new OAuth2Client(
        await getCredentials().web.client_id,
        await getCredentials().web.client_secret,
        await getCredentials().web.redirect_uris[0]);

    try {
        // Check if we have previously stored a token.
        const storedToken = await fs.readFile(tokenPath);
        oauth2Client.setCredentials(JSON.parse(storedToken));
        await oauth2Client.getAccessToken();
        return oauth2Client;
    } catch (error) {
        try {
            await renewToken(oauth2Client, scopes, tokenPath);
        } catch (error) {
            throw error;
        }
    }
}

async function renewToken(oauth2Client, scopes, tokenPath) {
    try {
        const authUrl = oauth2Client.generateAuthUrl({
            access_type: 'offline',
            scope: scopes
        });
        const authorizeSelenium = await runSeleniumCell(['log in Google','selenium authorize google access']);
        console.log('using selenium to authorize:'+ authUrl);
        const code = await authorizeSelenium(authUrl);
        return receiveCode(code, oauth2Client, tokenPath);
    } catch (error) {
        await errorFallback(error, oauth2Client, tokenPath);
    }
}

async function errorFallback(up, oauth2Client, tokenPath) {
    if (up.message.includes('ECONNREFUSED') || up.message.includes('find module') || up.message.includes('runSeleniumCell is not defined')) {
        console.log('can\'t authenticate with selenium, waiting for user input.');
        console.log('Authorize this app by visiting this url:', oauth2Client.generateAuthUrl({
            access_type: 'offline',
            scope: SCOPES
        }));
        const interface = readline.createInterface({
            input: process.stdin,
            output: process.stdout
        });
        const code = await new Promise(resolve => interface.question(
            'Enter the code from that page here: ', resolve));
        interface.close();
        return receiveCode(code, oauth2Client, tokenPath);
    } else {
        throw up;
    }
}

async function receiveCode(code, oauth2Client, tokenPath) {
    const token = await util.promisify(oauth2Client.getToken.bind(oauth2Client))(code);
    console.log('recieved token:'+ token.access_token);
    oauth2Client.setCredentials(token);
    await fs.writeFile(tokenPath, JSON.stringify(token, null, 4));
    console.log('Token stored to'+ tokenPath);
    return oauth2Client;
}

async function getCredentials() {
    try {
        const secretPath = path.join(TOKEN_DIR, 'client_secret.json');
        const secret = await fs.readFile(secretPath).then(data => JSON.parse(data).toString());
        return secret;
    } catch (error) {
        throw new Error('Unable to load client secret json');
    }
}

module.exports = authorize;

Overview

This code is a JavaScript module that utilizes various external libraries to handle OAuth2 authentication with Google Calendar. The module appears to be designed to handle errors and fallback scenarios, allowing the application to recover from connection issues.

Breakdown

Importing Libraries

The code begins by importing various Node.js libraries:

Loading Client Secrets

The code attempts to load client secrets from a local file (client_secret.json) located in the user's home directory. If the file does not exist, an error is caught and logged to the console.

Functions

There are four main functions in this code:

Error Fallback

The errorFallback function is triggered when an error occurs during OAuth2 authentication. It checks if the error message indicates a connection issue or a problem with importing Selenium cells. If so, it prompts the user to visit the authorization URL and enter the code received. Once the code is entered, the receiveCode function is called to exchange the code for a token.

Notes

The code is written in a asynchronous manner, using async/await syntax to handle promises. The process.stdout.isTTY property is used to detect if the console is a terminal, but it is commented out. The importer variable is imported from another module, but the runSeleniumCell import is commented out.