Skip to content

Commit c99a76c

Browse files
author
Eric Koleda
committed
Update README with latest features.
1 parent c08ae9a commit c99a76c

File tree

1 file changed

+124
-35
lines changed

1 file changed

+124
-35
lines changed

README.md

Lines changed: 124 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ expire. This library uses Apps Script's new
66
[StateTokenBuilder](https://developers.google.com/apps-script/reference/script/state-token-builder)
77
and `/usercallback` endpoint to handle the redirects.
88

9-
109
## Setup
1110

1211
This library is already published as an Apps Script, making it easy to include
@@ -49,7 +48,7 @@ exact URL that the service will use when performing the OAuth flow:
4948

5049
```js
5150
/**
52-
* Logs the redict URI to register.
51+
* Logs the redirect URI to register.
5352
*/
5453
function logRedirectUri() {
5554
var service = getService();
@@ -151,15 +150,15 @@ function authCallback(request) {
151150
}
152151
```
153152

154-
If the authorization URL was opened by the Apps Script UI (via a link, button,
155-
etc) it's possible to automatically close the window/tab using
156-
`window.top.close()`. You can see an example of this in the sample add-on's
153+
If the authorization URL was opened by the Apps Script UI (via a link, button,
154+
etc) it's possible to automatically close the window/tab using
155+
`window.top.close()`. You can see an example of this in the sample add-on's
157156
[Callback.html](samples/Add-on/Callback.html#L47).
158157

159158
### 4. Get the access token
160159

161160
Now that the service is authorized you can use its access token to make
162-
reqests to the API. The access token can be passed along with a `UrlFetchApp`
161+
requests to the API. The access token can be passed along with a `UrlFetchApp`
163162
request in the "Authorization" header.
164163

165164
```js
@@ -174,34 +173,66 @@ function makeRequest() {
174173
}
175174
```
176175

177-
## Compatibility
178176

179-
This library was designed to work with any OAuth2 provider, but because of small
180-
differences in how they implement the standard it may be that some APIs
181-
aren't compatible. If you find an API that it doesn't work with, open an issue or
182-
fix the problem yourself and make a pull request against the source code.
177+
### Logout
183178

184-
## Other features
179+
To logout the user or disconnect the service, perhaps so the user can select a
180+
different account, use the `reset()` method:
185181

186-
See below for some features of the library you may need to utilize depending on
187-
the specifics of the OAuth provider you are connecting to. See the [generated
188-
reference documentation](http://googlesamples.github.io/apps-script-oauth2/Service_.html)
189-
for a complete list of methods available.
182+
```js
183+
function logout() {
184+
var service = getDriveService()
185+
service.reset();
186+
}
187+
```
188+
189+
## Best practices
190190

191-
#### Resetting the access token
191+
### Caching
192192

193-
If you have an access token set and need to remove it from the property store
194-
you can remove it with the `reset()` function. Before you can call reset you
195-
need to set the property store.
193+
Scripts that use the library heavily should enable caching on the service, so as
194+
to not exhaust their `PropertiesService` quotas. To enable caching, simply add
195+
a `CacheService` cache when configuring the service:
196196

197197
```js
198-
function clearService(){
199-
OAuth2.createService('drive')
200-
.setPropertyStore(PropertiesService.getUserProperties())
201-
.reset();
202-
}
198+
return OAuth2.createService('Foo')
199+
.setPropertyStore(PropertiesService.getUserProperties())
200+
.setCache(CacheService.getUserCache())
201+
// ...
203202
```
204203

204+
Make sure to select a cache with the same scope (user, script, or document) as
205+
the property store you configured.
206+
207+
### Locking
208+
209+
A race condition can occur when two or more script executions are both trying to
210+
refresh an expired token at the same time. This is sometimes observed in
211+
[Gmail Add-ons](https://developers.google.com/gmail/add-ons/), where a user
212+
quickly paging through their email can trigger the same add-on multiple times.
213+
214+
To prevent this, use locking to ensure that only one execution is refreshing
215+
the token at a time. To enable locking, simply add a `LockService` lock when
216+
configuring the service:
217+
218+
```js
219+
return OAuth2.createService('Foo')
220+
.setPropertyStore(PropertiesService.getUserProperties())
221+
.setCache(CacheService.getUserCache())
222+
.setLock(LockService.getUserLock())
223+
// ...
224+
```
225+
226+
Make sure to select a lock with the same scope (user, script, or document) as
227+
the property store and cache you configured.
228+
229+
## Advanced configuration
230+
231+
See below for some features of the library you may need to utilize depending on
232+
the specifics of the OAuth provider you are connecting to. See the [generated
233+
reference documentation](http://googlesamples.github.io/apps-script-oauth2/Service_.html)
234+
for a complete list of methods available.
235+
205236
#### Setting the token format
206237

207238
OAuth services can return a token in two ways: as JSON or an URL encoded
@@ -227,11 +258,10 @@ See the [FitBit sample](samples/FitBit.gs) for the complete code.
227258

228259
#### Modifying the access token payload
229260

230-
Similar to Setting additional token headers, some services, such as the
231-
Smartsheet API, require you to
232-
[add a hash to the access token request payloads](http://smartsheet-platform.github.io/api-docs/?javascript#oauth-flow).
233-
The `setTokenPayloadHandler` method allows you to pass in a function to modify
234-
the payload of an access token request before the request is sent to the token
261+
Some OAuth providers, such as the Smartsheet API, require you to
262+
[add a hash to the access token request payloads]().
263+
The `setTokenPayloadHandler` method allows you to pass in a function to modify
264+
the payload of an access token request before the request is sent to the token
235265
endpoint:
236266

237267
```js
@@ -241,7 +271,59 @@ endpoint:
241271

242272
See the [Smartsheet sample](samples/Smartsheet.gs) for the complete code.
243273

244-
#### Service Accounts
274+
#### Storing token-related data
275+
276+
Some OAuth providers return IDs and other critical information in the callback
277+
URL along with the authorization code. While it's possible to capture and store
278+
these separately, they often have a lifecycle closely tied to that of the token
279+
and it makes sense to store them together. You can use `Service.getStorage()` to
280+
retrieve the token storage system for the service and set custom key-value
281+
pairs.
282+
283+
For example, the Harvest API returns the account ID of the authorized account
284+
in the callback URL. In the following code the account ID is extracted from the
285+
request parameters and saved saved into storage.
286+
287+
```js
288+
function authCallback(request) {
289+
var service = getService();
290+
var authorized = service.handleCallback(request);
291+
if (authorized) {
292+
// Gets the authorized account ID from the scope string. Assumes the
293+
// application is configured to work with single accounts. Has the format
294+
// "harvest:{ACCOUNT_ID}".
295+
var scope = request.parameter['scope'];
296+
var accountId = scope.split(':')[1];
297+
// Save the account ID in the service's storage.
298+
service.getStorage().setValue('Harvest-Account-Id', accountId);
299+
return HtmlService.createHtmlOutput('Success!');
300+
} else {
301+
return HtmlService.createHtmlOutput('Denied.');
302+
}
303+
}
304+
```
305+
306+
When making an authorized request the account ID is retrieved from storage and
307+
passed via a header.
308+
309+
```js
310+
if (service.hasAccess()) {
311+
// Retrieve the account ID from storage.
312+
var accountId = service.getStorage().getValue('Harvest-Account-Id');
313+
var url = 'https://api.harvestapp.com/v2/users/me';
314+
var response = UrlFetchApp.fetch(url, {
315+
headers: {
316+
'Authorization': 'Bearer ' + service.getAccessToken(),
317+
'User-Agent': 'Apps Script Sample',
318+
'Harvest-Account-Id': accountId
319+
}
320+
});
321+
```
322+
323+
Note that calling `Service.reset()` will remove all custom values from storage,
324+
in addition to the token.
325+
326+
#### Using service accounts
245327
246328
This library supports the service account authorization flow, also known as the
247329
[JSON Web Token (JWT) Profile](https://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-12).
@@ -257,11 +339,18 @@ authorization flow to obtain an access token. See the sample
257339
[`GoogleServiceAccount.gs`](samples/GoogleServiceAccount.gs) for more
258340
information.
259341
342+
## Compatibility
343+
344+
This library was designed to work with any OAuth2 provider, but because of small
345+
differences in how they implement the standard it may be that some APIs
346+
aren't compatible. If you find an API that it doesn't work with, open an issue
347+
or fix the problem yourself and make a pull request against the source code.
348+
260349
## Breaking changes
261350
262-
* Version 20 - Switched from using project keys to script IDs throughout the
263-
library. When upgrading from an older version, ensure the callback URL
264-
registered with the OAuth provider is updated to use the format
351+
* Version 20 - Switched from using project keys to script IDs throughout the
352+
library. When upgrading from an older version, ensure the callback URL
353+
registered with the OAuth provider is updated to use the format
265354
`https://script.google.com/macros/d/{SCRIPT ID}/usercallback`.
266355
* Version 22 - Renamed `Service.getToken_()` to `Service.getToken()`, since
267356
there OAuth providers that return important information in the token response.
@@ -271,6 +360,6 @@ there OAuth providers that return important information in the token response.
271360
### You do not have permission to call fetch
272361

273362
You are [setting explicit scopes](https://developers.google.com/apps-script/concepts/scopes#setting_explicit_scopes)
274-
in your manifest file but have forgotten to add the
363+
in your manifest file but have forgotten to add the
275364
`https://www.googleapis.com/auth/script.external_request` scope used by this library
276365
(and eventually the `UrlFetchApp` request you are making to an API).

0 commit comments

Comments
 (0)