ark_knight
ark_knight

Reputation: 41

Question related to google authentication flow in Nodejs

Okay, I will go crazy if someone doesn't explain to me what is going wrong. Its whether too simple or I am too stupid.

Here's the code, feel free to copy-paste it in your machines and RUN IT MORE THAN ONCE WITHOUT RESTARTING THE SERVER. I REPEAT, DO NOT RESTART THE SERVER.

const { OAuth2Client } = require('google-auth-library');
const router = require('express')();
const open = require('open');
const fs = require('fs');
const path = require('path');
const { google } = require('googleapis');
const { Buffer } = require('node:buffer');
var stream = require('stream');

const { redirect_uris, client_id, client_secret } = require('./config/config');

const filePath = path.join(__dirname + '/output/image.png');

async function pushToDrive(req, res) {
    try {
        const oAuth2Client = await getAuthenticatedClient();
        console.log("zero")
        // After acquiring an access_token, you may want to check on the audience, expiration,
        // or original scopes requested.  You can do that with the `getTokenInfo` method.
        // const tokenInfo = await oAuth2Client.getTokenInfo(
        //     oAuth2Client.credentials.access_token
        // );
        const drive = google.drive({
            version: 'v3',
            auth: oAuth2Client,
        });
        console.log("first")

        const response = await drive.files.create({
            resource: {
                mimeType: 'image/png'
            },
            media: {
                // mimeType: 'application/pdf',
                body: fs.createReadStream(filePath)
            },
            fields: 'id'
        });
        console.log("second")
        console.log(response.data.id);
        res.send(response.data.id);
        // return response.data.id;

    } catch (error) {
        console.error(error);
    }
}

/**
* Create a new OAuth2Client, and go through the OAuth2 content
* workflow.  Return the full client to the callback.
*/
function getAuthenticatedClient() {
    return new Promise((resolve, reject) => {
        // create an oAuth client to authorize the API call.  Secrets are kept in a `keys.json` file,
        // which should be downloaded from the Google Developers Console.
        try {
            const oAuth2Client = new OAuth2Client(
                client_id,
                client_secret,
                redirect_uris
            );

            // Generate the url that will be used for the consent dialog.
            const authorizeUrl = oAuth2Client.generateAuthUrl({
                access_type: 'offline',
                scope: 'https://www.googleapis.com/auth/drive.file',
            });

            open(authorizeUrl, { wait: false }).then(cp => cp.unref());

            router.get('/oauthcallback', async (req, res) => {
                // res.sendFile(path.join(__dirname, '/public/info.html'));
                // Now that we have the code, use that to acquire tokens.
                const r = await oAuth2Client.getToken(req.query.code);
                // Make sure to set the credentials on the OAuth2 client.
                oAuth2Client.setCredentials(r.tokens);
                console.info('Tokens acquired.');
                resolve(oAuth2Client);
                console.log("here2");
                res.send("You are authenticated");
                res.end();
                return resolve(oAuth2Client); 
            });
        } catch (error) {
            reject(error);
        }

    });
}

router.get('/xyz', pushToDrive);
router.listen(5000);

// pushToDrive();

// module.exports = { pushToDrive, getAuthenticatedClient, router };

The output I get in my backend console ->

Tokens acquired.
here2
zero
first
second
1rVk4Jjm0h_tO-x-BIDm1iWVDzZ4KfEJ3
Tokens acquired.
here2
Tokens acquired.
here2

As you can see, I got the file_id once - the first time I ran through the Drive upload. That's a success. But any subsequent tries stop at the resolution of oAuth2Client. WHY? If its the event loop, then someone explain me why its not returning control to the parent function. From running the debugger, I found out that Nodejs...just stops at resolve(oAuth2Client).

Upvotes: 0

Views: 343

Answers (1)

Fide
Fide

Reputation: 1197

There are two mistakes in your code: 1. You cannot define route inside another route. Your function pushToDrive which you call inside /xyz route defines new route to /oauthcallback. Sorry, you can - only have to take care the routes are called in proper sequence.

  1. Calling whatever after resolve in new Promise is a bit problematic, as it is called right after the whole function is started - it does not wait until something calls the promise.resolve

I would try something like this:

app.js

const oAuth2Client = new OAuth2Client(
                client_id,
                client_secret,
                redirect_uris
            );

const authorizeClient = () => {
            const authorizeUrl = oAuth2Client.generateAuthUrl({
                access_type: 'offline',
                scope: 'https://www.googleapis.com/auth/drive.file',
            });
            open(authorizeUrl, { wait: false }).then(cp => cp.unref());


async function pushToDrive(req, res) {
    try {
        const drive = google.drive({
            version: 'v3',
            auth: oAuth2Client,
        });
        console.log("first")

        const response = await drive.files.create({
            resource: {
                mimeType: 'image/png'
            },
            media: {
                // mimeType: 'application/pdf',
                body: fs.createReadStream(filePath)
            },
            fields: 'id'
        });
        console.log("second")
        console.log(response.data.id);
        res.send(response.data.id);

    } catch (error) {
        console.error(error);
    }
}

router.get('/oauthcallback', async (req, res) => {
                const r = await 
                oAuth2Client.getToken(req.query.code);
                oAuth2Client.setCredentials(r.tokens);
                console.info('Tokens acquired.');
            });

router.get('/xyz', pushToDrive);
router.listen(5000);

Upvotes: 1

Related Questions