3
votes

I am trying to programmatically access my Google cloud storage buckets for my Google Play developer account to get the statistics of the apps. I am getting an auth error even though I have added my service account to the play console and have given admin rights to it.


'use strict'

const { google } = require('googleapis')
const {Storage} = require('@google-cloud/storage');

const key = require('../Gcloud-service-account.json')
const scopes = 'https://www.googleapis.com/auth/devstorage.read_only'
const jwt = new google.auth.JWT(key.client_email, null, key.private_key, scopes)

process.env.GOOGLE_APPLICATION_CREDENTIALS = '../Gcloud-service-account.json';

jwt.authorize((err, response) => {

    const authCloudExplicit = async () => {
        // [START auth_cloud_explicit]
        // Imports the Google Cloud client library.



        const projectId = 'gplay-stats-nodejs'
        const keyFilename = '../Gcloud-service-account.json'
        const storage = new Storage({projectId, keyFilename});
        console.log('outside try')

        // Makes an authenticated API request.
        try {
          const [buckets] = await storage.getBuckets();
          console.log('inside try')
          buckets.forEach(bucket => {
            console.log(bucket.name);

          });
        } catch (err) {
          console.error('ERROR:', err);
        }
        // [END auth_cloud_explicit]
      };
      authCloudExplicit();

     //Reading data

      async function downloadFile() {
        // [START storage_download_file]
        // Imports the Google Cloud client library



      // Creates a client
        const storage = new Storage();

        /**
         * TODO(developer): Uncomment the following lines before running the sample.
         */
        const bucketName = 'pubsite__rev_05768497717432290806/stats/installs';
        const srcFilename = 'installs_package-id-of-my-app_201901_app_version.csv';
        const destFilename = '../download';

        const options = {

          destination: destFilename,
        };

        // Downloads the file
        await storage
          .bucket(bucketName)
          .file(srcFilename)
          .download(options);

        console.log(
          `gs://${bucketName}/${srcFilename} downloaded to ${destFilename}.`
        );

      }
      downloadFile();

    (err, result) => {
        console.log(err, result)
      }

  })
//Here is my service account json

{
  "type": "service_account",
  "project_id": "",
  "private_key_id": "",
  "private_key": "",
  "client_email": "",
  "client_id": "",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/service-account-email"
}

Removed some of the confidential fields.

I am getting this error:

code: 403,
  errors: [
    {
      domain: 'global',
      reason: 'forbidden',
      message: 'service-account-email' +
        'does not have storage.buckets.list access to project ' +
        '2763836207.'
    }
  ],
  response: PassThrough {
    _readableState: ReadableState {
      objectMode: false,
      highWaterMark: 16384,
      buffer: BufferList { head: null, tail: null, length: 0 },
      length: 0,
      pipes: null,
      pipesCount: 0,
      flowing: true,
      ended: true,
      endEmitted: true,
      reading: false,
      sync: false,
      needReadable: false,
      emittedReadable: false,
      readableListening: false,
      resumeScheduled: false,
      paused: false,
      emitClose: true,
      autoDestroy: false,
      destroyed: false,
      defaultEncoding: 'utf8',
      awaitDrain: 0,
      readingMore: false,
      decoder: null,
      encoding: null
    },
    readable: false,
    _events: [Object: null prototype] {
      prefinish: [Function: prefinish],
      error: [Array],
      data: [Function],
      end: [Function]
    },
    _eventsCount: 4,
    _maxListeners: undefined,
    _writableState: WritableState {
      objectMode: false,
      highWaterMark: 16384,
      finalCalled: false,
      needDrain: false,
      ending: true,
      ended: true,
      finished: true,
      destroyed: false,
      decodeStrings: true,
      defaultEncoding: 'utf8',
      length: 0,
      writing: false,
      corked: 0,
      sync: false,
      bufferProcessing: false,
      onwrite: [Function: bound onwrite],
      writecb: null,
      writelen: 0,
      bufferedRequest: null,
      lastBufferedRequest: null,
      pendingcb: 0,
      prefinished: true,
      errorEmitted: false,
      emitClose: true,
      autoDestroy: false,
      bufferedRequestCount: 0,
      corkedRequestsFree: [Object]
    },
    writable: false,
    allowHalfOpen: true,
    _transformState: {
      afterTransform: [Function: bound afterTransform],
      needTransform: false,
      transforming: false,
      writecb: null,
      writechunk: null,
      writeencoding: 'buffer'
    },
    statusCode: 403,
    statusMessage: 'Forbidden',
    request: {
      headers: [Object],
      href: 'https://www.googleapis.com/storage/v1/b?project=gplay-stats-nodejs'
    },
    body: '{\n "error": {\n  "errors": [\n   {\n    "domain": "global",\n    ' +
      '"reason": "forbidden",\n    "message": ' +
      '"service-account-email does ' +
      'not have storage.buckets.list access to project 2763836207."\n   }\n  ' +
      '],\n  "code": 403,\n  "message": ' +
      '"service-account-email does ' +
      'not have storage.buckets.list access to project 2763836207."\n }\n}\n',
    headers: {
      'alt-svc': 'quic=":443"; ma=2592000; v="46,43,39"',
      'cache-control': 'private, max-age=0',
      connection: 'close',
      'content-length': '400',
      'content-type': 'application/json; charset=UTF-8',
      date: 'Sun, 04 Aug 2019 21:46:39 GMT',
      expires: 'Sun, 04 Aug 2019 21:46:39 GMT',
      server: 'UploadServer',
      vary: 'Origin, X-Origin',
      'x-guploader-uploadid': 'AEnB2UqCDuQdbWsAmyTty5rImnOYxLB71xh5hf7-4boSY9c5d7cCZly5mQJsbJY57emgn9jyGDVKtlDM4jeUG07IJKl7I3RpxQ'
    },
    toJSON: [Function: toJSON]
  },
  message: 'service-account-email ' +
    'does not have storage.buckets.list access to project ' +
    '2763836207.'
}

1
Did you figure out how to do it?Kimble
@Kimble I had the same issue but tried again after 24 hours and now is OK. It seems like adding the service account to the play console users takes some time to work as expectedalek6dj

1 Answers

0
votes

What's the roles of your service account? It has to have only storage object viewer isn't it? This role allows your service account to view object in your bucket. But here you also need to list the bucket with this line

const [buckets] = await storage.getBuckets();

By the way, your service account also need the storage.buckets.list permission contained in role storageAdmin or projectViewer. Personally, I prefer and recommend the projectViewer because this role can't create/delete buckets, even if it has a view on the all resources of the project.

The best solution is to create a custom role, only with the right permission. Using your code isn't always a good solution, because library perform sometime calls that you don't handle and/or don't want. It's the case in Python, maybe also in node.