OAuth2 for Apps Script is a library for Google Apps Script that provides the
ability to create and authorize OAuth2 tokens as well as refresh them when they
expire. This library uses Apps Script's new
StateTokenBuilder
and /usercallback
endpoint to handle the redirects.
This library is already published as an Apps Script, making it easy to include in your project. To add it to your script, do the following in the Apps Script code editor:
- Click on the menu item "Resources > Libraries..."
- In the "Find a Library" text box, enter the project key "MswhXl8fVhTFUH_Q3UOJbXvxhMjh3Sh48" and click the "Select" button.
- Choose a version in the dropdown box (usually best to pick the latest version).
- Click the "Save" button.
Before you can start authenticating against an OAuth2 provider, you usually need to register your application and retrieve the client ID and secret. Often these registration screens require you to enter a "Redirect URI", which is the URL that users will be redirected to after they've authorized the token. For this library (and the Apps Script functionality in general) the URL will always be in the following format:
https://script.google.com/macros/d/{PROJECT KEY}/usercallback
Where {PROJECT KEY}
is the key of the script that is using this library. You
can find your script's project key in the Apps Script code editor by clicking on
the menu item "File > Project properties".
Using the library to generate an OAuth2 token has the following basic steps.
The OAuth2Service class contains the configuration information for a given OAuth2 provider, including it's endpoints, client IDs and secrets, etc. This information is not persisted to any data store, so you'll need to create this object each time you want to use it. The example below shows how to create a service for the Google Drive API.
function getDriveService() {
// Create a new service with the given name. The name will be used when
// persisting the authorized token, so ensure it is unique within the
// scope of the property store.
return OAuth2.createService('drive')
// Set the endpoint URLs, which are the same for all Google services.
.setAuthorizationBaseUrl('https://accounts.google.com/o/oauth2/auth')
.setTokenUrl('https://accounts.google.com/o/oauth2/token')
// Set the client ID and secret, from the Google Developers Console.
.setClientId('...')
.setClientSecret('...')
// Set the name of the callback function in the script referenced
// above that should be invoked to complete the OAuth flow.
.setCallbackFunction('authCallback')
// Set the property store where authorized tokens should be persisted.
.setPropertyStore(PropertiesService.getUserProperties())
// Set the scopes to request (space-separated for Google services).
.setScope('https://www.googleapis.com/auth/drive')
// Below are Google-specific OAuth2 parameters.
// Sets the login hint, which will prevent the account chooser screen
// from being shown to users logged in with multiple accounts.
.setParam('login_hint', Session.getActiveUser().getEmail())
// Requests offline access.
.setParam('access_type', 'offline')
// Forces the approval prompt every time. This is useful for testing,
// but not desirable in a production application.
.setParam('approval_prompt', 'force');
}
Apps Script UI's are not allowed to redirect the user's window to a new URL, so
you'll need to present the authorization URL as a link for the user to click.
The URL is generated by the service, using the function getAuthorizationUrl()
.
function showSidebar() {
var driveService = getDriveService();
if (!driveService.hasAccess()) {
var authorizationUrl = driveService.getAuthorizationUrl();
var template = HtmlService.createTemplate(
'<a href="<?= authorizationUrl ?>" target="_blank">Authorize</a>. ' +
'Reopen the sidebar when the authorization is complete.');
template.authorizationUrl = authorizationUrl;
var page = template.evaluate();
DocumentApp.getUi().showSidebar(page);
} else {
...
}
}
When the user completes the OAuth2 flow, the callback function you specified
for your service will be invoked. This callback function should pass its
request object to the service's handleCallback
function, and show a message
to the user.
function authCallback(request) {
var driveService = getDriveService();
var isAuthorized = driveService.handleCallback(request);
if (isAuthorized) {
return HtmlService.createHtmlOutput('Success! You can close this tab.');
} else {
return HtmlService.createHtmlOutput('Denied. You can close this tab');
}
}
Note: In an Apps Script UI it's not possible to automatically close a window or tab, so you'll need to direct the user to close it themselves.
Now that the service is authorized you can use its access token to make
reqests to the API. The access token can be passed along with a UrlFetchApp
request in the "Authorization" header.
function makeRequest() {
var driveService = getDriveService();
var response = UrlFetchApp.fetch('https://www.googleapis.com/drive/v2/files?maxResults=10', {
headers: {
Authorization: 'Bearer ' + driveService.getAccessToken()
}
});
...
}
This library was designed to work with any OAuth2 provider, but because of small differences in how they implement the standard it may be that some APIs aren't compatible. If you find an API that it does't work with, open an issue or fix the problem yourself and make a pull request against the source code.
If you have an access token set and need to remove it from the property store
you can remove it with the reset()
function. Before you can call reset you
need to set the property store.
function clearService(){
OAuth2.createService('drive')
.setPropertyStore(PropertiesService.getUserProperties())
.reset();
}
OAuth services can return a token in two ways: as JSON or an URL encoded
string. You can set which format the token is in with
setTokenFormat(tokenFormat)
. There are two ENUMS to set the mode:
TOKEN_FORMAT.FORM_URL_ENCODED
and TOKEN_FORMAT.JSON
. JSON is set as default
if no token format is chosen.
function gitService() {
return OAuth2.createService('git')
.setAuthorizationBaseUrl('https://github.com/login/oauth/authorize')
.setTokenUrl('https://github.com/login/oauth/access_token')
.setClientId('...')
.setClientSecret('...')
.setProjectKey('...')
.setCallbackFunction('authCallback')
.setPropertyStore(PropertiesService.getUserProperties())
.setScope('gist,repo,user')
.setTokenFormat(OAuth2.TOKEN_FORMAT.FORM_URL_ENCODED);
}