title: YouTube Analytics Goolge Authorization
body: <p>I am trying to authorise my server for calling the youtube analytics api via the module &quot;googleapis&quot; (<a href="https://www.npmjs.com/package/googleapis" rel="nofollow noreferrer">https://www.npmjs.com/package/googleapis</a>). I want to use the JWT to authorise. As you can see I get a forbidden error back from the api.</p>
  <p>I read a ton of posts and tinkered around for a whole while, but I can't figure out what is wrong.</p>
  <p>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.)</p>
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code> 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) =&gt; {
        if (error) console.log(error.errors);
        else {
          console.log(result);
          callback(result);
        }
      }
    );
  }
  
  jwtClient.authorize((error, result) =&gt; {
    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(() =&gt; {
        // worked :)
      });
    }
  });</code></pre>
  </div>
  </div>
  </p>
  <hr />
  <p>The response object may be helpful:</p>
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>  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&amp;start-date=2018-01-01&amp;end-date=2018-02-01&amp;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&amp;start-date=2018-01-01&amp;end-date=2018-02-01&amp;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' } ] }</code></pre>
  </div>
  </div>
  </p>
  <h2>Update 1</h2>
  <p>I updated the code a little, so that is analog to this example implementation: <a href="https://github.com/google/google-api-nodejs-client/blob/HEAD/samples/jwt.js" rel="nofollow noreferrer">https://github.com/google/google-api-nodejs-client/blob/HEAD/samples/jwt.js</a></p>
  <p>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?</p>
  <h2>Update 2</h2>
  <p>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?</p>