forked from googleworkspace/apps-script-oauth2
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
837 lines (484 loc) · 26 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Home</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Home</h1>
<h3> </h3>
<section>
<article><p>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
<a href="https://developers.google.com/apps-script/reference/script/state-token-builder">StateTokenBuilder</a>
and <code>/usercallback</code> endpoint to handle the redirects.</p>
<h2>Connecting to a Google API</h2>
<p>If you are trying to connect to a Google API from Apps Script you might not need
to use this library at all. Apps Script has a number of easy-to-use,
<a href="https://developers.google.com/apps-script/reference/calendar/">built-in services</a>, as well as a variety of
<a href="https://developers.google.com/apps-script/advanced/admin-sdk-directory">advanced services</a> that wrap existing Google REST APIs.</p>
<p>Even if your API is not covered by either, you can still use Apps Script to
obtain the OAuth2 token for you. Simply
<a href="https://developers.google.com/apps-script/concepts/manifests#editing_a_manifest">edit the script's manifest</a> to
<a href="https://developers.google.com/apps-script/concepts/scopes#setting_explicit_scopes">include the additional scopes</a> that your API requires.
When the user authorizes your script they will also be asked to approve those
additional scopes. Then use the method <a href="https://developers.google.com/apps-script/reference/script/script-app#getoauthtoken"><code>ScriptApp.getOAuthToken()</code></a>
in your code to access the OAuth2 access token the script has acquired and pass
it in the <code>Authorization</code> header of a <code>UrlFetchApp.fetch()</code> call.</p>
<p>Visit the sample <a href="samples/NoLibrary"><code>NoLibrary</code></a> to see an example of how this
can be done.</p>
<h2>Setup</h2>
<p>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:</p>
<ol>
<li>Click on the menu item "Resources > Libraries..."</li>
<li>In the "Find a Library" text box, enter the script ID
<code>1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF</code> and click the
"Select" button.</li>
<li>Choose a version in the dropdown box (usually best to pick the latest
version).</li>
<li>Click the "Save" button.</li>
</ol>
<p>Alternatively, you can copy and paste the files in the <a href="dist"><code>/dist</code></a> directory
directly into your script project.</p>
<p>If you are <a href="https://developers.google.com/apps-script/concepts/scopes#setting_explicit_scopes">setting explicit scopes</a>
in your manifest file, ensure that the following scope is included:</p>
<ul>
<li><code>https://www.googleapis.com/auth/script.external_request</code></li>
</ul>
<h2>Redirect URI</h2>
<p>Before you can start authenticating against an OAuth2 provider, you usually need
to register your application with that OAuth2 provider and obtain a client ID
and secret. Often a provider's registration screen requires you to enter a
"Redirect URI", which is the URL that the user's browser will be redirected to
after they've authorized access to their account at that provider.</p>
<p>For this library (and the Apps Script functionality in general) the URL will
always be in the following format:</p>
<pre><code>https://script.google.com/macros/d/{SCRIPT ID}/usercallback
</code></pre>
<p>Where <code>{SCRIPT ID}</code> is the ID of the script that is using this library. You
can find your script's ID in the Apps Script code editor by clicking on
the menu item "File > Project properties".</p>
<p>Alternatively you can call the service's <code>getRedirectUri()</code> method to view the
exact URL that the service will use when performing the OAuth flow:</p>
<pre class="prettyprint source lang-js"><code>/**
* Logs the redirect URI to register.
*/
function logRedirectUri() {
var service = getService_();
Logger.log(service.getRedirectUri());
}
</code></pre>
<h2>Usage</h2>
<p>Using the library to generate an OAuth2 token has the following basic steps.</p>
<h3>1. Create the OAuth2 service</h3>
<p>The OAuth2Service class contains the configuration information for a given
OAuth2 provider, including its 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.</p>
<p>Ensure the method is private (has an underscore at the end of the name) to
prevent clients from being able to call the method to read your client ID and
secret.</p>
<pre class="prettyprint source lang-js"><code>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.getEffectiveUser().getEmail())
// Requests offline access.
.setParam('access_type', 'offline')
// Consent prompt is required to ensure a refresh token is always
// returned when requesting offline access.
.setParam('prompt', 'consent');
}
</code></pre>
<h3>2. Direct the user to the authorization URL</h3>
<p>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 <code>getAuthorizationUrl()</code>.</p>
<pre class="prettyprint source lang-js"><code>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 {
// ...
}
}
</code></pre>
<h3>3. Handle the callback</h3>
<p>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 <code>handleCallback</code> function, and show a message
to the user.</p>
<pre class="prettyprint source lang-js"><code>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');
}
}
</code></pre>
<p>If the authorization URL was opened by the Apps Script UI (via a link, button,
etc) it's possible to automatically close the window/tab using
<code>window.top.close()</code>. You can see an example of this in the sample add-on's
<a href="samples/Add-on/Callback.html#L47">Callback.html</a>.</p>
<h3>4. Get the access token</h3>
<p>Now that the service is authorized you can use its access token to make
requests to the API. The access token can be passed along with a <code>UrlFetchApp</code>
request in the "Authorization" header.</p>
<pre class="prettyprint source lang-js"><code>function makeRequest() {
var driveService = getDriveService_();
var response = UrlFetchApp.fetch('https://www.googleapis.com/drive/v2/files?maxResults=10', {
headers: {
Authorization: 'Bearer ' + driveService.getAccessToken()
}
});
// ...
}
</code></pre>
<h3>Logout</h3>
<p>To logout the user or disconnect the service, perhaps so the user can select a
different account, use the <code>reset()</code> method:</p>
<pre class="prettyprint source lang-js"><code>function logout() {
var service = getDriveService_()
service.reset();
}
</code></pre>
<h2>Best practices</h2>
<h3>Token storage</h3>
<p>In almost all cases you'll want to persist the OAuth tokens after you retrieve
them. This prevents having to request access from the user every time you want
to call the API. To do so, make sure you set a properties store when you define
your service:</p>
<pre class="prettyprint source lang-js"><code>return OAuth2.createService('Foo')
.setPropertyStore(PropertiesService.getUserProperties())
// ...
</code></pre>
<p>Apps Script has <a href="https://developers.google.com/apps-script/reference/properties/properties-service">property stores</a> scoped to the user, script,
or document. In most cases you'll want to choose user-scoped properties, as it
is most common to have each user of your script authorize access to their own
account. However there are uses cases where you'd want to authorize access to
a shared resource and then have all users of the script (or on the same
document) share that access.</p>
<p>When using a service account or 2-legged OAuth flow, where users aren't prompted
for authorization, storing tokens is still beneficial as there can be rate
limits on generating new tokens. However there are edge cases where you need to
generate lots of different tokens in a short amount of time, and persisting
those tokens to properties can exceed your <code>PropertiesService</code> quota. In those
cases you can omit any form of token storage and just retrieve new ones as
needed.</p>
<h3>Caching</h3>
<p>Scripts that use the library heavily should enable caching on the service, so as
to not exhaust their <code>PropertiesService</code> quotas. To enable caching, simply add
a <code>CacheService</code> cache when configuring the service:</p>
<pre class="prettyprint source lang-js"><code>return OAuth2.createService('Foo')
.setPropertyStore(PropertiesService.getUserProperties())
.setCache(CacheService.getUserCache())
// ...
</code></pre>
<p>Make sure to select a cache with the same scope (user, script, or document) as
the property store you configured.</p>
<h3>Locking</h3>
<p>A race condition can occur when two or more script executions are both trying to
refresh an expired token at the same time. This is sometimes observed in
<a href="https://developers.google.com/gmail/add-ons/">Gmail Add-ons</a>, where a user
quickly paging through their email can trigger the same add-on multiple times.</p>
<p>To prevent this, use locking to ensure that only one execution is refreshing
the token at a time. To enable locking, simply add a <code>LockService</code> lock when
configuring the service:</p>
<pre class="prettyprint source lang-js"><code>return OAuth2.createService('Foo')
.setPropertyStore(PropertiesService.getUserProperties())
.setCache(CacheService.getUserCache())
.setLock(LockService.getUserLock())
// ...
</code></pre>
<p>Make sure to select a lock with the same scope (user, script, or document) as
the property store and cache you configured.</p>
<h2>Advanced configuration</h2>
<p>See below for some features of the library you may need to utilize depending on
the specifics of the OAuth provider you are connecting to. See the <a href="http://googleworkspace.github.io/apps-script-oauth2/Service_.html">generated
reference documentation</a>
for a complete list of methods available.</p>
<h4>Setting the token format</h4>
<p>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
<code>setTokenFormat(tokenFormat)</code>. There are two ENUMS to set the mode:
<code>TOKEN_FORMAT.FORM_URL_ENCODED</code> and <code>TOKEN_FORMAT.JSON</code>. JSON is set as default
if no token format is chosen.</p>
<h4>Setting additional token headers</h4>
<p>Some services, such as the FitBit API, require you to set an Authorization
header on access token requests. The <code>setTokenHeaders()</code> method allows you
to pass in a JavaScript object of additional header key/value pairs to be used
in these requests.</p>
<pre class="prettyprint source lang-js"><code>.setTokenHeaders({
'Authorization': 'Basic ' + Utilities.base64Encode(CLIENT_ID + ':' + CLIENT_SECRET)
});
</code></pre>
<p>See the <a href="samples/FitBit.gs">FitBit sample</a> for the complete code.</p>
<h4>Setting the token HTTP method</h4>
<p>Almost all services use the <code>POST</code> HTTP method when retrieving the access token,
but a few services deviate from the spec and use the <code>PUT</code> method instead. To
accomodate those cases you can use the <code>setTokenMethod()</code> method to specify the
HTTP method to use when making the request.</p>
<h4>Modifying the access token payload</h4>
<p>Some OAuth providers, such as the Smartsheet API, require you to
<a href="https://smartsheet-platform.github.io/api-docs/#request-an-access-token">add a hash to the access token request payloads</a>.
The <code>setTokenPayloadHandler</code> method allows you to pass in a function to modify
the payload of an access token request before the request is sent to the token
endpoint:</p>
<pre class="prettyprint source lang-js"><code>// Set the handler for modifying the access token request payload:
.setTokenPayloadHandler(myTokenHandler)
</code></pre>
<p>See the <a href="samples/Smartsheet.gs">Smartsheet sample</a> for the complete code.</p>
<h4>Storing token-related data</h4>
<p>Some OAuth providers return IDs and other critical information in the callback
URL along with the authorization code. While it's possible to capture and store
these separately, they often have a lifecycle closely tied to that of the token
and it makes sense to store them together. You can use <code>Service.getStorage()</code> to
retrieve the token storage system for the service and set custom key-value
pairs.</p>
<p>For example, the Harvest API returns the account ID of the authorized account
in the callback URL. In the following code the account ID is extracted from the
request parameters and saved saved into storage.</p>
<pre class="prettyprint source lang-js"><code>function authCallback(request) {
var service = getService_();
var authorized = service.handleCallback(request);
if (authorized) {
// Gets the authorized account ID from the scope string. Assumes the
// application is configured to work with single accounts. Has the format
// "harvest:{ACCOUNT_ID}".
var scope = request.parameter['scope'];
var accountId = scope.split(':')[1];
// Save the account ID in the service's storage.
service.getStorage().setValue('Harvest-Account-Id', accountId);
return HtmlService.createHtmlOutput('Success!');
} else {
return HtmlService.createHtmlOutput('Denied.');
}
}
</code></pre>
<p>When making an authorized request the account ID is retrieved from storage and
passed via a header.</p>
<pre class="prettyprint source lang-js"><code>if (service.hasAccess()) {
// Retrieve the account ID from storage.
var accountId = service.getStorage().getValue('Harvest-Account-Id');
var url = 'https://api.harvestapp.com/v2/users/me';
var response = UrlFetchApp.fetch(url, {
headers: {
'Authorization': 'Bearer ' + service.getAccessToken(),
'User-Agent': 'Apps Script Sample',
'Harvest-Account-Id': accountId
}
});
</code></pre>
<p>Note that calling <code>Service.reset()</code> will remove all custom values from storage,
in addition to the token.</p>
<h4>Passing additional parameters to the callback function</h4>
<p>There are occasionally cases where you need to preserve some data through the
OAuth flow, so that it is available in your callback function. Although you
could use the token storage mechanism discussed above for that purpose, writing
to the PropertiesService is expensive and not neccessary in the case where the
user doesn't start or fails to complete the OAuth flow.</p>
<p>As an alternative you can store small amounts of data in the OAuth2 <code>state</code>
token, which is a standard mechanism for this purpose. To do so, pass an
optional hash of parameter names and values to the <code>getAuthorizationUrl()</code>
method:</p>
<pre class="prettyprint source lang-js"><code>var authorizationUrl = getService_().getAuthorizationUrl({
// Pass the additional parameter "lang" with the value "fr".
lang: 'fr'
});
</code></pre>
<p>These values will be stored along-side Apps Script's internal information in the
encypted <code>state</code> token, which is passed in the authorization URL and passed back
to the redirect URI. The <code>state</code> token is automatically decrypted in the
callback function and you can access your parameters using the same
<code>request.parameter</code> field used in web apps:</p>
<pre class="prettyprint source lang-js"><code>function authCallback(request) {
var lang = request.parameter.lang;
// ...
}
</code></pre>
<h4>Using service accounts</h4>
<p>This library supports the service account authorization flow, also known as the
<a href="https://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-12">JSON Web Token (JWT) Profile</a>.
This is a two-legged OAuth flow that doesn't require a user to visit a URL and
authorize access.</p>
<p>One common use for service accounts with Google APIs is
<a href="https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority">domain-wide delegation</a>.
This process allows a G Suite domain administrator to grant an
application access to all the users within the domain. When the application
wishes to access the resources of a particular user, it uses the service account
authorization flow to obtain an access token. See the sample
<a href="samples/GoogleServiceAccount.gs"><code>GoogleServiceAccount.gs</code></a> for more
information.</p>
<h4>Using alternative grant types</h4>
<p>Although optimized for the authorization code (3-legged) and service account
(JWT bearer) flows, this library supports arbitrary flows using the
<code>setGrantType()</code> method. Use <code>setParam()</code> or <code>setTokenPayloadHandler()</code> to add
fields to the token request payload, and <code>setTokenHeaders()</code> to add any required
headers.</p>
<p>The most common of these is the <code>client_credentials</code> grant type, which often
requires that the client ID and secret are passed in the Authorization header.
When using this grant type, if you set a client ID and secret using
<code>setClientId()</code> and <code>setClientSecret()</code> respectively then an
<code>Authorization: Basic ...</code> header will be added to the token request
automatically, since this is what most OAuth2 providers require. If your
provider uses a different method of authorization then don't set the client ID
and secret and add an authorization header manually.</p>
<p>See the sample <a href="samples/TwitterAppOnly.gs"><code>TwitterAppOnly.gs</code></a> for a working
example.</p>
<h2>Frequently Asked Questions</h2>
<h3>How can I connect to multiple OAuth services?</h3>
<p>The service name passed in to the <code>createService</code> method forms part of the key
used when storing and retrieving tokens in the property store. To connect to
multiple services merely ensure they have different service names. Often this
means selecting a service name that matches the API the user will authorize:</p>
<pre class="prettyprint source lang-js"><code>function run() {
var gitHubService = getGitHubService_();
var mediumService = getMediumService_();
// ...
}
function getGitHubService_() {
return OAuth2.createService('GitHub')
// GitHub settings ...
}
function getMediumService_() {
return OAuth2.createService('Medium')
// Medium settings ...
}
</code></pre>
<p>Occasionally you may need to make multiple connections to the same API, for
example if your script is trying to copy data from one account to another. In
those cases you'll need to devise your own method for creating unique service
names:</p>
<pre class="prettyprint source lang-js"><code>function run() {
var copyFromService = getGitHubService_('from');
var copyToService = getGitHubService_('to');
// ...
}
function getGitHubService_(label) {
return OAuth2.createService('GitHub_' + label)
// GitHub settings ...
}
</code></pre>
<p>You can list all of the service names you've previously stored tokens for using
<code>OAuth2.getServiceNames(propertyStore)</code>.</p>
<h2>Compatibility</h2>
<p>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 doesn't work with, open an issue
or fix the problem yourself and make a pull request against the source code.</p>
<p>This library is designed for server-side OAuth flows, and client-side flows with
implicit grants (<code>response_type=token</code>) are not supported.</p>
<h2>Breaking changes</h2>
<ul>
<li>Version 20 - Switched from using project keys to script IDs throughout the
library. When upgrading from an older version, ensure the callback URL
registered with the OAuth provider is updated to use the format
<code>https://script.google.com/macros/d/{SCRIPT ID}/usercallback</code>.</li>
<li>Version 22 - Renamed <code>Service.getToken_()</code> to <code>Service.getToken()</code>, since
there OAuth providers that return important information in the token response.</li>
</ul>
<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>
in your manifest file but have forgotten to add the
<code>https://www.googleapis.com/auth/script.external_request</code> scope used by this library
(and eventually the <code>UrlFetchApp</code> request you are making to an API).</p></article>
</section>
<section>
<header>
<h2>OAuth2.js</h2>
</header>
<article>
<div class="container-overview">
<div class="description">Contains the methods exposed by the library, and performs
any required setup.</div>
<dl class="details">
</dl>
</div>
</article>
</section>
<section>
<header>
<h2>Service.js</h2>
</header>
<article>
<div class="container-overview">
<div class="description">Contains the Service_ class.</div>
<dl class="details">
</dl>
</div>
</article>
</section>
<section>
<header>
<h2>Storage.js</h2>
</header>
<article>
<div class="container-overview">
<div class="description">Contains classes used to persist data and access it.</div>
<dl class="details">
</dl>
</div>
</article>
</section>
<section>
<header>
<h2>Utilities.js</h2>
</header>
<article>
<div class="container-overview">
<div class="description">Contains utility methods used by the library.</div>
<dl class="details">
</dl>
</div>
</article>
</section>
</div>
<nav>
<h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Service_.html">Service_</a></li><li><a href="Storage_.html">Storage_</a></li></ul><h3>Global</h3><ul><li><a href="global.html#createService">createService</a></li><li><a href="global.html#decodeJwt_">decodeJwt_</a></li><li><a href="global.html#encodeChallenge_">encodeChallenge_</a></li><li><a href="global.html#encodeJwt_">encodeJwt_</a></li><li><a href="global.html#encodeUrlSafeBase64NoPadding_">encodeUrlSafeBase64NoPadding_</a></li><li><a href="global.html#extend_">extend_</a></li><li><a href="global.html#getRedirectUri">getRedirectUri</a></li><li><a href="global.html#getServiceNames">getServiceNames</a></li><li><a href="global.html#TOKEN_FORMAT">TOKEN_FORMAT</a></li><li><a href="global.html#toLowerCaseKeys_">toLowerCaseKeys_</a></li></ul>
</nav>
<br class="clear">
<footer>
Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.11</a>
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>