node.jsgoogle-cloud-platformgoogle-oauthgoogle-analytics-apigoogle-api-nodejs-client

Do Google Cloud API Libraries support Oauth2 on behalf of other users?


I'd like to access the Google Analytics Admin API on behalf of other users. I have a valid oAuth2 access token from the Oauth2 dialog flow with authentication window popup.

Google offers new Cloud Client Libraries or older Google API Client Libraries for access to their services.

Google then says here:

Cloud Client Libraries are the recommended option for accessing Cloud APIs programmatically, where available

However, these newer cloud Libraries don't seem to offer OAuth2 authentication for access on behalf of another user (not myself).

For example, I'd like to use the Google Analytics Admin API.

I visit the Google Analytics Admin Cloud Client library and the documentation tells me to authenticate by downloading the JSON file from my credential area (This works only if I access my own services but NOT to make API calls on behalf of a given user)

  // Instantiates a client using default credentials.
  // TODO(developer): uncomment and use the following line in order to
  //  manually set the path to the service account JSON file instead of
  //  using the value from the GOOGLE_APPLICATION_CREDENTIALS environment
  //  variable.
  // const analyticsAdminClient = new analyticsAdmin.AnalyticsAdminServiceClient(
  //     {keyFilename: "your_key_json_file_path"});
  const analyticsAdminClient = new analyticsAdmin.AnalyticsAdminServiceClient();

  // Calls listAccounts() method of the Google Analytics Admin API and prints
  // the response for each account.
  const [accounts] = await analyticsAdminClient.listAccounts();

So then I visit the older Google API Client library for Analytics Admin.

It supports Oauth2 access_tokens ... but first thing it says:

Note: Google provides multiple libraries for this service. This library is in maintenance mode, and will continue to be made available for users who have existing applications. If you're building a new application, or modernizing a legacy application, please use @google-analytics/admin instead. The @google-analytics/admin library is faster, easier to use, and better maintained.

I want the newer better libraries, but do they support Oauth2 on behalf of other users?

I know that I can acquire oAuth2 access_tokens with the Google Auth Library. But I don't know how to use these tokens then to authorize with the newer cloud libraries.


Solution

  • They do offer oauth2 they just don't offer a sample for it, but I do.

    // npm install googleapis@105 @google-cloud/local-auth@2.1.0 --save
    // npm install @google-analytics/data
    
    const fs = require('fs');
    const path = require('path');
    const process = require('process');
    const {authenticate} = require('@google-cloud/local-auth');
    const {google} = require('googleapis');
    const {BetaAnalyticsDataClient} = require("@google-analytics/data");
    
    // If modifying these scopes, delete token.json.
    const SCOPES = ['https://www.googleapis.com/auth/analytics.readonly'];
    
    // Token File Name
    const TOKEN_FILE = 'token.json'
    
    // 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 = path.join(process.cwd(), TOKEN_FILE);
    const CREDENTIALS_PATH = 'C:\\Development\\FreeLance\\GoogleSamples\\Credentials\\Credentials.json';
    
    
    /**
     * Reads previously authorized credentials from the save file.
     *
     * @return {Promise<OAuth2Client|null>}
     */
    async function loadSavedCredentialsIfExist() {
        try {
            const content = fs.readFileSync(TOKEN_PATH,{encoding:'utf8', flag:'r'});
            const credentials = JSON.parse(content);
            return google.auth.fromJSON(credentials);
        } catch (err) {
            return null;
        }
    }
    
    /**
     * Serializes credentials to a file compatible with GoogleAUth.fromJSON.
     *
     * @param {OAuth2Client} client
     * @return {Promise<void>}
     */
    async function saveCredentials(client) {
    
        const content = fs.readFileSync(CREDENTIALS_PATH, {encoding:'utf8', flag:'r'});
        const keys = JSON.parse(content);
    
        const key = keys.installed || keys.web;
        const payload = JSON.stringify({
            type: 'authorized_user',
            client_id: key.client_id,
            client_secret: key.client_secret,
            refresh_token: client.credentials.refresh_token,
        });
        await fs.writeFileSync(TOKEN_PATH, payload);
    }
    
    /**
     * Load or request or authorization to call APIs.
     *
     */
    async function authorize() {
        let client = await loadSavedCredentialsIfExist();
        if (client) {
            return client;
        }
        client = await authenticate({
            scopes: SCOPES,
            keyfilePath: CREDENTIALS_PATH,
        });
        if (client.credentials) {
            await saveCredentials(client);
        }
        return client;
    }
    
    /**
     * Runs a Google Analytics report
     * For a given property id
     */
    async function runReport(authClient ) {
    
        // Imports the Google Analytics Data API client library.
        const {BetaAnalyticsDataClient} = require('@google-analytics/data');
        // Load the credentials.
        const content = fs.readFileSync('token.json', {encoding:'utf8', flag:'r'});
        const keys = JSON.parse(content);
        const analyticsDataClient = new BetaAnalyticsDataClient(
            {  credentials: keys}
        );
    
        var propertyId = '250796939';
    
        // Runs a simple report.
        async function runReport() {
            const [response] = await analyticsDataClient.runReport({
                property: `properties/${propertyId}`,
                dateRanges: [
                    {
                        startDate: '2020-03-31',
                        endDate: 'today',
                    },
                ],
                dimensions: [
                    {
                        name: 'city',
                    },
                ],
                metrics: [
                    {
                        name: 'activeUsers',
                    },
                ],
            });
    
            console.log('Report result:');
            response.rows.forEach(row => {
                console.log(row.dimensionValues[0], row.metricValues[0]);
            });
        }
    
        await runReport();
    }
    
    
    
    
    
    authorize().then(runReport).catch(console.error);