2

I am trying to authorise my server for calling the youtube analytics api via the module "googleapis" (https://www.npmjs.com/package/googleapis). I want to use the JWT to authorise. As you can see I get a forbidden error back from the api.

I read a ton of posts and tinkered around for a whole while, but I can't figure out what is wrong.

Is my code ok? Otherwise it as to be a google cloud related issue. (E.g. I did not set up the roles properly there.)

 import fs from 'fs';
import { google } from 'googleapis';

// read credentials from file
const credentialsFromFile = fs.readFileSync(`${__dirname}/../credentials.json`);
const credentials = JSON.parse(credentialsFromFile.toString());

const scopes = [
  'https://www.googleapis.com/auth/youtube.readonly',
  'https://www.googleapis.com/auth/yt-analytics-monetary.readonly',
  'https://www.googleapis.com/auth/youtubepartner',
];

// get an authorized client
const jwtClient = new google.auth.JWT(
  credentials.client_email,
  null,
  credentials.private_key,
  scopes
);

const youtubeAnalyticsClient = google.youtubeAnalytics({
  version: 'v1',
  auth: jwtClient,
});

function runQuery(callback) {
  youtubeAnalyticsClient.reports.query(
    {
      auth: jwtClient,
      ids: 'channel==MINE',
      'start-date': '2018-01-01',
      'end-date': '2018-02-01',
      metrics: 'views',
    },
    (error, result) => {
      if (error) console.log(error.errors);
      else {
        console.log(result);
        callback(result);
      }
    }
  );
}

jwtClient.authorize((error, result) => {
  if (error) console.log(error);
  else {
/*
  { access_token: '{THE ACCESS TOKEN}',
  token_type: 'Bearer',
  expiry_date: 1522070090000,
  refresh_token: 'jwt-placeholder' } */
    console.log(result);

    runQuery(() => {
      // worked :)
    });
  }
});

The response object may be helpful:

  response: 
   { status: 403,
     statusText: 'Forbidden',
     headers: 
      { vary: 'X-Origin, Origin,Accept-Encoding',
        'content-type': 'application/json; charset=UTF-8',
        date: 'Mon, 26 Mar 2018 10:39:33 GMT',
        expires: 'Mon, 26 Mar 2018 10:39:33 GMT',
        'cache-control': 'private, max-age=0',
        'x-content-type-options': 'nosniff',
        'x-frame-options': 'SAMEORIGIN',
        'x-xss-protection': '1; mode=block',
        server: 'GSE',
        'alt-svc': 'hq=":443"; ma=2592000; quic=51303432; quic=51303431; quic=51303339; quic=51303335,quic=":443"; ma=2592000; v="42,41,39,35"',
        'accept-ranges': 'none',
        connection: 'close' },
     config: 
      { adapter: [Function: httpAdapter],
        transformRequest: [Object],
        transformResponse: [Object],
        timeout: 0,
        xsrfCookieName: 'XSRF-TOKEN',
        xsrfHeaderName: 'X-XSRF-TOKEN',
        maxContentLength: 2147483648,
        validateStatus: [Function],
        headers: [Object],
        method: 'get',
        url: 'https://www.googleapis.com/youtube/analytics/v1/reports',
        paramsSerializer: [Function],
        data: undefined,
        params: [Object] },
     request: 
      ClientRequest {
        domain: null,
        _events: [Object],
        _eventsCount: 6,
        _maxListeners: undefined,
        output: [],
        outputEncodings: [],
        outputCallbacks: [],
        outputSize: 0,
        writable: false,
        _last: true,
        upgrading: false,
        chunkedEncoding: false,
        shouldKeepAlive: false,
        useChunkedEncodingByDefault: false,
        sendDate: false,
        _removedConnection: false,
        _removedContLen: false,
        _removedTE: false,
        _contentLength: 0,
        _hasBody: true,
        _trailer: '',
        finished: true,
        _headerSent: true,
        socket: [Object],
        connection: [Object],
        _header: 'GET /youtube/analytics/v1/reports?ids=channel%3D%3DMINE&start-date=2018-01-01&end-date=2018-02-01&metrics=views HTTP/1.1\r\nAccept: application/json, text/plain, */*\r\nAuthorization: Bearer ya29.c.El6KBUokXWf8yjN1LXk04b3FTGa1jadNEmvbyLBQfVmK9KIbxwxA6e88m_OujuTcyrJW60ojwnKiNfH-9E2iegmwlDOdKI8VxORrniGIt9gc_FGp2tvi2GLabzTri74b\r\nUser-Agent: google-api-nodejs-client/1.3.2\r\nHost: www.googleapis.com\r\nConnection: close\r\n\r\n',
        _onPendingData: [Function: noopPendingOutput],
        agent: [Object],
        socketPath: undefined,
        timeout: undefined,
        method: 'GET',
        path: '/youtube/analytics/v1/reports?ids=channel%3D%3DMINE&start-date=2018-01-01&end-date=2018-02-01&metrics=views',
        _ended: true,
        res: [Object],
        aborted: undefined,
        timeoutCb: null,
        upgradeOrConnect: false,
        parser: null,
        maxHeadersCount: null,
        _redirectable: [Object],
        [Symbol(outHeadersKey)]: [Object] },
     data: { error: [Object] } },
  code: 403,
  errors: [ { domain: 'global', reason: 'forbidden', message: 'Forbidden' } ] }

Update 1

I updated the code a little, so that is analog to this example implementation: https://github.com/google/google-api-nodejs-client/blob/HEAD/samples/jwt.js

I get an authorised client as seen in the code, but the same error occurs. I am pretty sure I messed up the settings in the google cloud settings. Is it necessary to do the domain verification at google cloud when developing locally?

Update 2

Even with OAuth2 Authentication (passing a code as cli arg after allowing scopes in the browser) I get the forbidden-error. Seems like I need to enable it in the cloud dev console, but how do I do that?

Community
  • 1
  • 1
Neskews
  • 764
  • 1
  • 10
  • 23

0 Answers0