google-api-nodejs-client icon indicating copy to clipboard operation
google-api-nodejs-client copied to clipboard

OAuth2 globally defined client & concurrent backend function execution

Open SteveShengStar opened this issue 4 years ago • 1 comments

I have an inquiry also related to declaring oauth2Client as a variable globally in a JS file vs. locally inside a function. Replica of this Stack Overflow post I created: https://stackoverflow.com/questions/68145083/oauth2-globally-defined-client-concurrent-backend-function-execution

START OF QUESTION: I have some confusion about integrating OAuth 2 flow (Google APIs) into my app. I have declared a Oauth2 client variable (globally) as shown below:

var auth2;

//Initializes the Sign-In client.
var initClient = function() {
    gapi.load('auth2', function(){
        // Retrieve the singleton for the GoogleAuth library and set up the client.
        auth2 = gapi.auth2.init({
            client_id: 'CLIENT_ID.apps.googleusercontent.com'
        });
        // Attach the click handler to the sign-in button
        auth2.attachClickHandler('signin-button', {}, onSuccess, onFailure);
    });
};

If I were to use Google Calendar API, I would do this:

const doSomethingWithGCalendar = (token) => {
    auth2.setCredentials({
       access_token: token
    });
        
    const calendar = google.calendar({version: 'v3', auth: auth2});
    // Execute some actions here
}

I am confused about how node.js handles this. After all, the auth2 client variable is globally declared (I picture in my head as one, single instance). However, there will be multiple users using my app, so the line .setCredentials and subsequent Google Calendar API calls will run, probably in multiple threads and concurrently, I'm assuming.

They should be running concurrently in the node.js server, but they're using the same globally declared variable auth2. How would that work ?

Should I, instead, declare a local variable inside doSomethingWithGCalendar() like this:

const doSomethingWithGCalendar = (token) => {
    const auth2 = new OAuth2Client(
        {client_id: 'CLIENT_ID.apps.googleusercontent.com'}
    );
    // Set Credentials/tokens and then run Google Calendar API operations ...

SteveShengStar avatar Jul 03 '21 02:07 SteveShengStar

I found a solution in another issue: https://github.com/googleapis/google-api-nodejs-client/issues/2080#issuecomment-633267633

Hey folks! I will admit doing this right is a tad tricky. I highly reccomened using a distinct OAuth2Client object for each user credential (access token and refresh token) being stored. After the user logs in, and the call comes back with credentials to your endpoint I would:

* Instantiate a new OAuth2Client using the keyfile you've downloaded (same for everyone)

* Use `setCredentials` to store the appropriate access token and refresh token

* Store the `OAuth2` object in a map, using the userId as a key

The OAuth2 objects are stateful (for better or for worse). They will manage refreshing the access token automatically for you assuming there is a refresh token in place. If you were to try using a single object and switch contexts, there is too much risk that you'll leak credentials meant for one user to another user. Then, very bad things happen.

By keeping these in separate stateful objects in a map, there's far less risk of cross-contaminating keys. Additionally, if you choose to store your access and refresh token securely, it makes rehydrating them later a fair bit easier.

This is all more complex than I'd like it to be :/ Does this explanation make sense?

adriaandotcom avatar Jul 21 '21 15:07 adriaandotcom