@@ -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 )
77and ` /usercallback ` endpoint to handle the redirects.
88
9-
109## Setup
1110
1211This 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 */
5453function 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
161160Now 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 `
163162request 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
207238OAuth 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
235265endpoint:
236266
237267``` js
@@ -241,7 +271,59 @@ endpoint:
241271
242272See 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
246328This 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
258340information.
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
267356there 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
273362You 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