@@ -79,7 +79,7 @@ <h2>Redirect URI</h2><p>Before you can start authenticating against an OAuth2 pr
7979< p > Alternatively you can call the service's < code > getRedirectUri()</ code > method to view the
8080exact URL that the service will use when performing the OAuth flow:</ p >
8181< pre class ="prettyprint source lang-js "> < code > /**
82- * Logs the redict URI to register.
82+ * Logs the redirect URI to register.
8383 */
8484function logRedirectUri() {
8585 var service = getService();
@@ -154,12 +154,12 @@ <h3>1. Create the OAuth2 service</h3><p>The OAuth2Service class contains the con
154154 } else {
155155 return HtmlService.createHtmlOutput('Denied. You can close this tab');
156156 }
157- }</ code > </ pre > < p > If the authorization URL was opened by the Apps Script UI (via a link, button,
158- etc) it's possible to automatically close the window/tab using
159- < code > window.top.close()</ code > . You can see an example of this in the sample add-on's
157+ }</ code > </ pre > < p > If the authorization URL was opened by the Apps Script UI (via a link, button,
158+ etc) it's possible to automatically close the window/tab using
159+ < code > window.top.close()</ code > . You can see an example of this in the sample add-on's
160160< a href ="samples/Add-on/Callback.html#L47 "> Callback.html</ a > .</ p >
161161< h3 > 4. Get the access token</ h3 > < p > 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 < code > UrlFetchApp</ code >
162+ requests to the API. The access token can be passed along with a < code > UrlFetchApp</ code >
163163request in the "Authorization" header.</ p >
164164< pre class ="prettyprint source lang-js "> < code > function makeRequest() {
165165 var driveService = getDriveService();
@@ -169,22 +169,37 @@ <h3>4. Get the access token</h3><p>Now that the service is authorized you can us
169169 }
170170 });
171171 // ...
172- }</ code > </ pre > < h2 > Compatibility</ h2 > < p > This library was designed to work with any OAuth2 provider, but because of small
173- differences in how they implement the standard it may be that some APIs
174- aren't compatible. If you find an API that it doesn't work with, open an issue or
175- fix the problem yourself and make a pull request against the source code.</ p >
176- < h2 > Other features</ h2 > < p > See below for some features of the library you may need to utilize depending on
172+ }</ code > </ pre > < h3 > Logout</ h3 > < p > To logout the user or disconnect the service, perhaps so the user can select a
173+ different account, use the < code > reset()</ code > method:</ p >
174+ < pre class ="prettyprint source lang-js "> < code > function logout() {
175+ var service = getDriveService()
176+ service.reset();
177+ }</ code > </ pre > < h2 > Best practices</ h2 > < h3 > Caching</ h3 > < p > Scripts that use the library heavily should enable caching on the service, so as
178+ to not exhaust their < code > PropertiesService</ code > quotas. To enable caching, simply add
179+ a < code > CacheService</ code > cache when configuring the service:</ p >
180+ < pre class ="prettyprint source lang-js "> < code > return OAuth2.createService('Foo')
181+ .setPropertyStore(PropertiesService.getUserProperties())
182+ .setCache(CacheService.getUserCache())
183+ // ...</ code > </ pre > < p > Make sure to select a cache with the same scope (user, script, or document) as
184+ the property store you configured.</ p >
185+ < h3 > Locking</ h3 > < p > A race condition can occur when two or more script executions are both trying to
186+ refresh an expired token at the same time. This is sometimes observed in
187+ < a href ="https://developers.google.com/gmail/add-ons/ "> Gmail Add-ons</ a > , where a user
188+ quickly paging through their email can trigger the same add-on multiple times.</ p >
189+ < p > To prevent this, use locking to ensure that only one execution is refreshing
190+ the token at a time. To enable locking, simply add a < code > LockService</ code > lock when
191+ configuring the service:</ p >
192+ < pre class ="prettyprint source lang-js "> < code > return OAuth2.createService('Foo')
193+ .setPropertyStore(PropertiesService.getUserProperties())
194+ .setCache(CacheService.getUserCache())
195+ .setLock(LockService.getUserLock())
196+ // ...</ code > </ pre > < p > Make sure to select a lock with the same scope (user, script, or document) as
197+ the property store and cache you configured.</ p >
198+ < h2 > Advanced configuration</ h2 > < p > See below for some features of the library you may need to utilize depending on
177199the specifics of the OAuth provider you are connecting to. See the < a href ="http://googlesamples.github.io/apps-script-oauth2/Service_.html "> generated
178200reference documentation</ a >
179201for a complete list of methods available.</ p >
180- < h4 > Resetting the access token</ h4 > < p > If you have an access token set and need to remove it from the property store
181- you can remove it with the < code > reset()</ code > function. Before you can call reset you
182- need to set the property store.</ p >
183- < pre class ="prettyprint source lang-js "> < code > function clearService(){
184- OAuth2.createService('drive')
185- .setPropertyStore(PropertiesService.getUserProperties())
186- .reset();
187- }</ code > </ pre > < h4 > Setting the token format</ h4 > < p > OAuth services can return a token in two ways: as JSON or an URL encoded
202+ < h4 > Setting the token format</ h4 > < p > OAuth services can return a token in two ways: as JSON or an URL encoded
188203string. You can set which format the token is in with
189204< code > setTokenFormat(tokenFormat)</ code > . There are two ENUMS to set the mode:
190205< code > TOKEN_FORMAT.FORM_URL_ENCODED</ code > and < code > TOKEN_FORMAT.JSON</ code > . JSON is set as default
@@ -196,15 +211,52 @@ <h4>Setting additional token headers</h4><p>Some services, such as the FitBit AP
196211< pre class ="prettyprint source lang-js "> < code > .setTokenHeaders({
197212 'Authorization': 'Basic ' + Utilities.base64Encode(CLIENT_ID + ':' + CLIENT_SECRET)
198213});</ code > </ pre > < p > See the < a href ="samples/FitBit.gs "> FitBit sample</ a > for the complete code.</ p >
199- < h4 > Modifying the access token payload</ h4 > < p > Similar to Setting additional token headers, some services, such as the
200- Smartsheet API, require you to
201- < a href ="http://smartsheet-platform.github.io/api-docs/?javascript#oauth-flow "> add a hash to the access token request payloads</ a > .
202- The < code > setTokenPayloadHandler</ code > method allows you to pass in a function to modify
203- the payload of an access token request before the request is sent to the token
214+ < h4 > Modifying the access token payload</ h4 > < p > Some OAuth providers, such as the Smartsheet API, require you to
215+ < a href =""> add a hash to the access token request payloads</ a > .
216+ The < code > setTokenPayloadHandler</ code > method allows you to pass in a function to modify
217+ the payload of an access token request before the request is sent to the token
204218endpoint:</ p >
205219< pre class ="prettyprint source lang-js "> < code > // Set the handler for modifying the access token request payload:
206220.setTokenPayloadHandler(myTokenHandler)</ code > </ pre > < p > See the < a href ="samples/Smartsheet.gs "> Smartsheet sample</ a > for the complete code.</ p >
207- < h4 > Service Accounts</ h4 > < p > This library supports the service account authorization flow, also known as the
221+ < h4 > Storing token-related data</ h4 > < p > Some OAuth providers return IDs and other critical information in the callback
222+ URL along with the authorization code. While it's possible to capture and store
223+ these separately, they often have a lifecycle closely tied to that of the token
224+ and it makes sense to store them together. You can use < code > Service.getStorage()</ code > to
225+ retrieve the token storage system for the service and set custom key-value
226+ pairs.</ p >
227+ < p > For example, the Harvest API returns the account ID of the authorized account
228+ in the callback URL. In the following code the account ID is extracted from the
229+ request parameters and saved saved into storage.</ p >
230+ < pre class ="prettyprint source lang-js "> < code > function authCallback(request) {
231+ var service = getService();
232+ var authorized = service.handleCallback(request);
233+ if (authorized) {
234+ // Gets the authorized account ID from the scope string. Assumes the
235+ // application is configured to work with single accounts. Has the format
236+ // "harvest:{ACCOUNT_ID}".
237+ var scope = request.parameter['scope'];
238+ var accountId = scope.split(':')[1];
239+ // Save the account ID in the service's storage.
240+ service.getStorage().setValue('Harvest-Account-Id', accountId);
241+ return HtmlService.createHtmlOutput('Success!');
242+ } else {
243+ return HtmlService.createHtmlOutput('Denied.');
244+ }
245+ }</ code > </ pre > < p > When making an authorized request the account ID is retrieved from storage and
246+ passed via a header.</ p >
247+ < pre class ="prettyprint source lang-js "> < code > if (service.hasAccess()) {
248+ // Retrieve the account ID from storage.
249+ var accountId = service.getStorage().getValue('Harvest-Account-Id');
250+ var url = 'https://api.harvestapp.com/v2/users/me';
251+ var response = UrlFetchApp.fetch(url, {
252+ headers: {
253+ 'Authorization': 'Bearer ' + service.getAccessToken(),
254+ 'User-Agent': 'Apps Script Sample',
255+ 'Harvest-Account-Id': accountId
256+ }
257+ });</ code > </ pre > < p > Note that calling < code > Service.reset()</ code > will remove all custom values from storage,
258+ in addition to the token.</ p >
259+ < h4 > Using service accounts</ h4 > < p > This library supports the service account authorization flow, also known as the
208260< a href ="https://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-12 "> JSON Web Token (JWT) Profile</ a > .
209261This is a two-legged OAuth flow that doesn't require a user to visit a URL and
210262authorize access.</ p >
@@ -216,16 +268,20 @@ <h4>Service Accounts</h4><p>This library supports the service account authorizat
216268authorization flow to obtain an access token. See the sample
217269< a href ="samples/GoogleServiceAccount.gs "> < code > GoogleServiceAccount.gs</ code > </ a > for more
218270information.</ p >
271+ < h2 > Compatibility</ h2 > < p > This library was designed to work with any OAuth2 provider, but because of small
272+ differences in how they implement the standard it may be that some APIs
273+ aren't compatible. If you find an API that it doesn't work with, open an issue
274+ or fix the problem yourself and make a pull request against the source code.</ p >
219275< h2 > Breaking changes</ h2 > < ul >
220- < li > Version 20 - Switched from using project keys to script IDs throughout the
221- library. When upgrading from an older version, ensure the callback URL
222- registered with the OAuth provider is updated to use the format
276+ < li > Version 20 - Switched from using project keys to script IDs throughout the
277+ library. When upgrading from an older version, ensure the callback URL
278+ registered with the OAuth provider is updated to use the format
223279< code > https://script.google.com/macros/d/{SCRIPT ID}/usercallback</ code > .</ li >
224280< li > Version 22 - Renamed < code > Service.getToken_()</ code > to < code > Service.getToken()</ code > , since
225281there OAuth providers that return important information in the token response.</ li >
226282</ ul >
227283< h2 > Troubleshooting</ h2 > < h3 > You do not have permission to call fetch</ h3 > < p > You are < a href ="https://developers.google.com/apps-script/concepts/scopes#setting_explicit_scopes "> setting explicit scopes</ a >
228- in your manifest file but have forgotten to add the
284+ in your manifest file but have forgotten to add the
229285< code > https://www.googleapis.com/auth/script.external_request</ code > scope used by this library
230286(and eventually the < code > UrlFetchApp</ code > request you are making to an API).</ p > </ article >
231287 </ section >
@@ -597,7 +653,7 @@ <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Service_.
597653< br class ="clear ">
598654
599655< footer >
600- Documentation generated by < a href ="https://github.com/jsdoc3/jsdoc "> JSDoc 3.5.5</ a > on Sat Feb 17 2018 16:28:49 GMT-0500 (STD )
656+ Documentation generated by < a href ="https://github.com/jsdoc3/jsdoc "> JSDoc 3.5.5</ a > on Wed Mar 21 2018 10:13:02 GMT-0400 (DST )
601657</ footer >
602658
603659< script > prettyPrint ( ) ; </ script >
0 commit comments