JoJo
JoJo

Reputation: 117

How to resolve 403 error with Service Account and Google Drive API

I'm trying to write a client-side JS script that will fetch images from my gDrive to display on a website using a service account. I created the service account and added and enabled the google Drive API for the project. But when I run the script, I'm getting a 403 error: Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup. I think it has to do with permissions or scopes maybe? I've looked at several other stack overflows and can't seem to figure it out.

Getting a 403 - Forbidden for Google Service Account

Google Drive service account returns 403 usageLimits

Some of them mention adding roles or scopes, but I can't figure out how to add them or which ones I need to add. Is a GSuite account mandatory? It sounds like I would need to go into the GSuite admin console to add the scopes? I would like to not have to sign up for an account, as it's not free. Any help would be appreciated. My code looks like the following:


function gDrive() {
  function init(callback) {
    authorizeClient(getJWT()).then(function(token) {
      loadClient(token, callback);
    });
  }

  /* Retrieve a signed JWT */
  function getJWT() {
    // Prepare header, payload, and key
    let oHeader = {
      "alg": "RS256",
      "typ": "JWT"
    };
    let sHeader = JSON.stringify(oHeader);
    let oPayload = {
      "iss": "SERVICE ACCOUNT EMAIL",
      "sub": "SERVICE ACCOUNT EMAIL",
      "aud": "https://www.googleapis.com/oauth2/v3/token",
      "iat": KJUR.jws.IntDate.getNow(),
      "exp": KJUR.jws.IntDate.get("now + 1hour"),
      "scope": "https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/drive.appdata https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/drive.metadata https://www.googleapis.com/auth/drive.metadata.readonly https://www.googleapis.com/auth/drive.photos.readonly https://www.googleapis.com/auth/drive.readonly"
    };
    let sPayload = JSON.stringify(oPayload);
    let privKey = "-----BEGIN PRIVATE KEY-----BLAH BLAH BLAH\n-----END PRIVATE KEY-----\n";

    // Sign JWT
    return signedJWS = KJUR.jws.JWS.sign(null, sHeader, sPayload, privKey);
  }

  /* Http POST to Google Auth api */
  function authorizeClient(JWS) {
    // Request access token
    const url = "https://www.googleapis.com/oauth2/v3/token";
    let encodedData = "";
    let encodedDataPairs = [];
    encodedDataPairs.push(encodeURIComponent("grant_type") + '=' + encodeURIComponent("urn:ietf:params:oauth:grant-type:jwt-bearer"));
    encodedDataPairs.push(encodeURIComponent("assertion") + '=' + encodeURIComponent(JWS));
    encodedData = encodedDataPairs.join('&').replace(/%20/g, '+');

    const params = {
      headers: {"content-type":"application/x-www-form-urlencoded"},
      body: encodedData,
      method: "POST"
    };

    return fetch(url, params).then(accessTokenSucces).then(returnToken).catch(accessTokenFailed);
  }

  function accessTokenSucces(data) {
    console.log("Retrieved access token");
    return data.json();
  }

  function returnToken(resp) {
    return resp.access_token;
  }

  function accessTokenFailed(error) {
    console.log("Requesting access token failed: " + error);
  }

  function loadClient(accessToken, callback) {
    gapi.load('client', function() {
      console.log("loading client");
      gapi.client.setToken(accessToken);
      console.log("set access token");
      return gapi.client.load("https://content.googleapis.com/discovery/v1/apis/drive/v3/rest").then(clientLoadSuccessful).then(callback).catch(clientLoadFailed);
    })
  }

  function clientLoadSuccessful() {
    console.log("Client loaded");
    return Promise.resolve();
  }

  function clientLoadFailed(error) {
    console.log("Loading Client failed: " + error);
    return Promise.reject();
  }

  function fetchAllImages(fileName, chapter, callback) {
    console.log("fetching images");
    let initialRequest = gapi.client.drive.files.list({"q": "mimeType contains \"image\" and name contains '" 
                                                       + fileName + "_ch" + chapter + "'"});
    retrievePageOfFiles(initialRequest, [], fileName, chapter);

    function retrievePageOfFiles(request, result) {
      request.execute(function(resp) {
        result = result.concat(resp.files);
        let nextPageToken = resp.nextPageToken;
        if (nextPageToken) {
          request = gapi.client.drive.files.list({
            "pageToken": nextPageToken,
            "q": "mimeType contains \"image\" and name contains '" + fileName + "_ch" + chapter + "'"
          });
          retrievePageOfFiles(request, result);
        } else {
          console.log("Images retrieved");
          callback(result);
        }
      }).catch(function(err) {
        console.log("Could not retrieve images: " + err);
      });
    }
  }

  return {
    init: init,
    fetchAllImages: fetchAllImages
  };
}

gDrive().init(runApp);

function runApp() {
console.log("Running App");
    gDrive().fetchAllImages("FILENAME", "1", imageCallback);
}

function imageCallback(data) {
    console.log("Images Retrieved!");
    console.log(data);
}

Upvotes: 2

Views: 1680

Answers (1)

Tanaike
Tanaike

Reputation: 201713

  • When your script is run, the error of Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup. occurs.
  • You want to remove this error.

If my understanding is correct, how about this modification? I think that the access token retrieved with your script can be used. So please modify your script as follows.

From:

gapi.client.setToken(accessToken);

To:

gapi.client.setToken({access_token: accessToken});

Reference:

If this was not the direct solution of your issue, I apologize.

Upvotes: 1

Related Questions