Ben Barbersmith

runner · developer · entrepreneur

OAuth in Google Apps Script with UrlFetchApp

— Last updated on Jun 17, 2021

With Google Apps Script, OAuth is super simple for integrated services like Drive (DriveApp), Gmail (MailApp), Google Sheets (SpreadsheetsApp), etc.

If you use an integrated service that requires specific scopes and permissions via OAuth, Google Apps Script automatically prompts users for those permissions whenever a function from the project is run.

For most project and most users, this means they’ll see a popup first time they use the Google Apps Script project and they’ll be asked to give permissions to access their Drive, their Google Sheets, etc.

But what if you need to use a Google service that isn’t integrated, such as Google Cloud Storage?

The good news is that you can use UrlFetchApp to send HTTP requests to any REST API you like.

The bad news is that OAuth isn’t magically handled by Google Apps Script. If you use UrlFetchApp and hit the Google Cloud Storage API, you’ll just get 403 Forbidden errors. The users won’t be prompted to grant access, so the requests will be unauthorized.

But it is possible to use OAuth with UrlFetchApp. And even though it’s undocumented, it’s way easier than you might think.

Adding custom scopes to Google Apps Script projects

Apps Script already magically handles OAuth for integrated services like Drive, Gmail, etc.

It’s also possible to add arbitrary Google OAuth scopes to the project that correspond to other Google services — and if you do that, Google will prompt for those permissions at the same time that it prompts for the automatically detected scopes!

And if you do that, you can later use the auth tokens with URLFetchApp.

So, how do you add OAuth scopes to a Google Apps Script project?

First, edit project settings:

☑️ Show “appsscript.json” manifest file in editor"

Next, explicitly add the scope to the manifest file, appsscript.json. In the example below, I’ll add the readonly scope for Google Cloud Storage (https://www.googleapis.com/auth/devstorage.read_only):

{
  "timeZone": "Europe/London",
  "dependencies": {},
  "oauthScopes": [
    "https://www.googleapis.com/auth/script.external_request",
    "https://www.googleapis.com/auth/devstorage.read_only",
  ],
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8"
}

Don’t forget to include https://www.googleapis.com/auth/script.external_request to enable UrlFetchApp, plus any other scopes you need.

Next time you or your users run a function from your project, Apps Script will automagically ask for permission to grant access to these additional scopes.

Using the magic Apps Script OAuth bearer token with UrlFetchApp

Okay, so you’ve added the scopes you need for the relevant Google REST APIs. Your users will grant permissions to those scopes next time they use the project. Now you’re ready to use the OAuth access token that Apps Script has automatically granted.

To get that OAuth access token, call ScriptApp.getOAuthToken(). You can then use it in your REST API requests in the usual way!

When you make REST API requests using UrlFetchApp, pass a headers argument that uses the access token in the usual HTTP style. Like this:

const response = UrlFetchApp.fetch(url, {
  method: 'GET',
  headers: { Authorization: `Bearer ${ScriptApp.getOAuthToken()}` },
})

Et voilà!

Example: Accessing Google Cloud Storage from Google Apps Script

Putting it all together, how can we access Google Cloud Storage from Apps Script?

  1. Edit project settings to show the appsscript.json manifest file in the editor
  2. Add the required Google Cloud Storage scope to appsscript.json (e.g. https://www.googleapis.com/auth/devstorage.read_only)
  3. Use the Google Cloud Storage REST API, for example you can list objects in a bucket like this:
var CLOUD_STORAGE_BUCKET = "example-bucket-id";
var response = UrlFetchApp.fetch(
  `https://storage.googleapis.com/storage/v1/b/${CLOUD_STORAGE_BUCKET}/o`,
  {
    method: "GET",
    headers: { Authorization: `Bearer ${ScriptApp.getOAuthToken()}` },
  }
);
var json = JSON.parse(response.getContentText());

Summary

If you follow the steps in this post:

Please enjoy!