OAuth in Google Apps Script with UrlFetchApp
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?
- Edit project settings to show the
appsscript.json
manifest file in the editor - Add the required Google Cloud Storage scope to
appsscript.json
(e.g.https://www.googleapis.com/auth/devstorage.read_only
) - 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:
- OAuth will be magically handled via Apps Script
- Your users will have the same native experience as usual when granting permissions, even to Google services not integrated into Apps Script
- You can now use these extra scopes with any Google service that provides a REST API
Please enjoy!