0

This is Yet another error 403 on Google Sheets API. Um trying to read the data from a Spreadsheet and expose it on a website, using some custom HTML filters.

So, there is this question: Error 403 on Google Sheets API

Still, I cannot figure out what to do. I have setup my service account in the google cloud platform, granted it permissions to access my Spreadsheet - I have even made my SpreadSheet accessible via link (but that is restricted to emails from my organization, so it seems useless anyway). I've also activated domain-wide delegation for my service account - didn't make any difference at all. I'm pretty sure the issue is not related to a wrong key or some typo because, when I remove the access to the service email from the sheet, I steel get a 403 error, but it comes with a message saying the caller doesn't have access to that spreadsheet.

People keep mentioning OAuth, but I don't want to use that, since I'm intending to use a simple API access, as in this Google GitHub example. I'm using my business account, so there might be some issue related to that.

Here is the HTML (which I'm running from a simple HTTP server on python, not directly, given the fact the GAPI doesn't handle localhost/ sources very well):

<!DOCTYPE html>
<html>
  <head>
    <title>Google Sheets API Quickstart</title>
    <meta charset="utf-8" />
  </head>
  <body>
    <p>Google Sheets API Quickstart</p>

    <!--Add buttons to initiate auth sequence and sign out-->
    <button id="authorize_button" style="display: none;">Authorize</button>
    <button id="signout_button" style="display: none;">Sign Out</button>

    <pre id="content" style="white-space: pre-wrap;"></pre>

    <script type="text/javascript">
      // Client ID and API key from the Developer Console
      var API_KEY = 'My-API-KEY';
      var DISCOVERY_DOCS = ["https://sheets.googleapis.com/$discovery/rest?version=v4"];
      var SCOPES = "https://www.googleapis.com/auth/spreadsheets.readonly";

      function handleClientLoad() {
        gapi.load('client', initClient);
      }

      function initClient() {
        gapi.client.init({
          apiKey: API_KEY,
          discoveryDocs: DISCOVERY_DOCS
        }).then(function(){listMajors();})
      }

      function appendPre(message) {
        var pre = document.getElementById('content');
        var textContent = document.createTextNode(message + '\n');
        pre.appendChild(textContent);
      }

      function listMajors() {
        gapi.client.sheets.spreadsheets.values.get({
          spreadsheetId: 'my-spreadsheet-id',
          range: 'Sheet1!A1:A10',
        }).then(function(response) {
          var range = response.result;
          if (range.values.length > 0) {
            appendPre('Name, Major:');
            for (i = 0; i < range.values.length; i++) {
              var row = range.values[i];
              // Print columns A and E, which correspond to indices 0 and 4.
              appendPre(row[0] + ', ' + row[4]);
            }
          } else {
            appendPre('No data found.');
          }
        }, function(response) {
          appendPre('Error: ' + response.result.error.message);
        });
      }
    </script>

    <script async defer src="https://apis.google.com/js/api.js"
      onload="this.onload=function(){};handleClientLoad()"
      onreadystatechange="if (this.readyState === lete') this.onload()">
    </script>
  </body>
</html>


Does anyone have anyclue on what I'm missing, or what else can I try?

Edit: sharing the spreadsheet publicly is not an option. The is a business email, and, therefore, the only option I've got here is sharing it domain wide, as I've already stated above. Also, this is a business spreadsheet, and therefofe contains sentitive data. Doesn't really make sense to expose it publicly. Besides that, I've already shared the spreadsheet with the service email. What I'm trying to achieve, after all, is to read the data from the spreadsheet without using oauth, and emdding it on a website (I know there is a built-in embed tool, but that doesn't suit me because I need to add a html filter).

Community
  • 1
  • 1
Lucas Lima
  • 832
  • 11
  • 23
  • You are trying to access to Google Spreadsheet using the service account with the script in your question. If my understanding is correct, can I ask you about where the service account is used in your script? If I misunderstood your question, I apologize. – Tanaike Feb 13 '20 at 00:14
  • You understood it properly. I don't declare it directly. I didn't even see in the docs some guidance to use it. I don't know how shoould it be done. – Lucas Lima Feb 13 '20 at 01:14
  • Thank you for replying. When the service account is used, the users are not required to log in to Google. In this case, I think that you can create HTML including Javascript without the python server. But I'm not sure about your goal. So can I ask you about your goal? – Tanaike Feb 13 '20 at 01:33
  • Yeah, the python server was used only for local testing purposes. My goal is to read the data from a Spreadsheet and show it on a site - but I need to have a filter, so the default Google Spreadsheet embedding options doont suit me. – Lucas Lima Feb 13 '20 at 01:41
  • Thank you for replying. For example, in your actual situation, the Spreadsheet can shared publicly as the readonly? If you can do, how about retrieving the values from the shared Spreadsheet? In this case, the values can be retrieved with the API key, and the script is simpler than that using the service account. – Tanaike Feb 13 '20 at 01:55
  • That is not possible. I'm using my business account, so there is no "public" spreadsheet. The most I can get it to share it for everyone inside my company - but, even then, it doesn't solve my issue, given the service email is not recognized as being an email from my company's email domains. – Lucas Lima Feb 13 '20 at 03:52
  • Thank you for replying. I apologize that my comment was not useful for your situation. This is due to my poor skill. I deeply apologize for this. – Tanaike Feb 13 '20 at 04:56
  • Thank you for replying. I would like to study more and more. – Tanaike Feb 13 '20 at 07:46
  • Service Accounts are for server-to-server interactions. You cannot use them in the browser. You should share the spreadsheet with all users who should be able to access it. Is there a reason you cannot do that? – Iamblichus Feb 13 '20 at 09:23
  • What do you mean by "cannot use them on the browser"? If I am to embed the sheets on a website, what am I supposed to do, them? Other than that, the documentation I linked seems to state otherwise, at least for the translation service. Regarding the sharing, I've already stated that I cannot share spreadsheet publicly because this is the Gsuite for business, and, therefore, the most broad share available is for all users in my email domain - which does not include the service account. Also, I've already shared the spreadsheet with the service email. I don't know what else am I to share. – Lucas Lima Feb 13 '20 at 13:40
  • `If I am to embed the sheets on a website, what am I supposed to do, them?`: not use a Service Account but a regular account. `Other than that, the documentation I linked seems to state otherwise, at least for the translation service`: of course you can use the browser to connect to the API, but you cannot use a Service Account for that. You'll see that in the workflow, the user has to explicitly authorize the application, something that you cannot do with a Service Account. Please notice that in the documentation you referenced there is no mention about Service Accounts. – Iamblichus Feb 14 '20 at 13:29
  • I'm not sure I follow. When you mention user, are you implying I have to use oath? Because, in the linked example, they are using the API_KEY to perform the access - I'm supposing one user does not have to authorize the access like that. Or, I'm completely misunderstanding. Can you enlighten me on this? – Lucas Lima Feb 14 '20 at 13:33
  • Since you want to access a resource that is not public, the `API_KEY` won't be enough. You have to use OAuth. In the linked example they are accessing public resources. That's not your case. – Iamblichus Feb 14 '20 at 15:36
  • Sorry for the delay. Would you please write that down as an answer, so I can accept it? – Lucas Lima Feb 17 '20 at 14:58

1 Answers1

0

If you try to access a Spreadsheet that is not public, you will have to use OAuth 2.0. Since it is not a public resource, you have to use the credentials of an account that has access to this resource.

In the example you provided, they are accessing a public resource, so the API key is enough. That's not your case.

Reference:

Iamblichus
  • 18,540
  • 2
  • 11
  • 27
  • Just to clarify - if I want to embed a given Spreadsheet on my HTML page, the user accessing the page will be prompted to grant my page access, from his Google user, to the Spreadsheet? – Lucas Lima Feb 18 '20 at 15:46
  • @LucasLima how are you embedding the spreadsheet to your page? Is that via an iframe? – Iamblichus Feb 18 '20 at 16:00
  • 1
    I'm not actually trying to embed it. I'm trying to read the data from the spreadsheet, and then present it on a HTML table - the reason for that is I want the user to be able to filter the values, and, AFAIK, standard embedding doesn't allow that. So, although I have not got that far, I don't think that will be a thing in my process. Advice is most welcome, btw – Lucas Lima Feb 18 '20 at 16:01
  • @LucasLima What you could do in this situation is to make a request to your server, in which you could use your Service Account to [complete the authorization flow](https://developers.google.com/identity/protocols/OAuth2ServiceAccount) and request the desired data from the spreadsheet, and finally, send this data back to the client. I don't know what technologies (if any) you're using server-side though, so I'm afraid I can't be more precise. I'd suggest you to create another question regarding this, while providing more details about your case. – Iamblichus Feb 18 '20 at 21:12
  • Thank you for your suggestion. I didn't know of the existence of such a thing. That said, considering they don't show an option to do it using JS on a browser (I intend to embed it on a Google Site, BTW), I will access the spreadsheet using a Google Script, and expose it as a webapp, which will just be accessible when logged in a business account from our domain. – Lucas Lima Feb 19 '20 at 01:32
  • @LucasLima Sure, if you're open to using an Apps Script Web App, it will make the process much easier. Good luck! – Iamblichus Feb 19 '20 at 09:31