@@ -370,6 +370,20 @@ Service_.prototype.setExpirationMinutes = function(expirationMinutes) {
370
370
return this ;
371
371
} ;
372
372
373
+ /**
374
+ * Sets the OAuth2 grant_type to use when obtaining an access token. This does
375
+ * not need to be set when using either the authorization code flow (AKA
376
+ * 3-legged OAuth) or the service account flow. The most common usage is to set
377
+ * it to "client_credentials" and then also set the token headers to include
378
+ * the Authorization header required by the OAuth2 provider.
379
+ * @param {string } grantType The OAuth2 grant_type value.
380
+ * @return {Service_ } This service, for chaining.
381
+ */
382
+ Service_ . prototype . setGrantType = function ( grantType ) {
383
+ this . grantType_ = grantType ;
384
+ return this ;
385
+ } ;
386
+
373
387
/**
374
388
* Gets the authorization URL. The first step in getting an OAuth2 token is to
375
389
* have the user visit this URL and approve the authorization request. The
@@ -425,29 +439,14 @@ Service_.prototype.handleCallback = function(callbackRequest) {
425
439
'Token URL' : this . tokenUrl_
426
440
} ) ;
427
441
var redirectUri = getRedirectUri ( this . scriptId_ ) ;
428
- var headers = {
429
- 'Accept' : this . tokenFormat_
430
- } ;
431
- if ( this . tokenHeaders_ ) {
432
- headers = extend_ ( headers , this . tokenHeaders_ ) ;
433
- }
434
- var tokenPayload = {
442
+ var payload = {
435
443
code : code ,
436
444
client_id : this . clientId_ ,
437
445
client_secret : this . clientSecret_ ,
438
446
redirect_uri : redirectUri ,
439
447
grant_type : 'authorization_code'
440
448
} ;
441
- if ( this . tokenPayloadHandler_ ) {
442
- tokenPayload = this . tokenPayloadHandler_ ( tokenPayload ) ;
443
- }
444
- var response = UrlFetchApp . fetch ( this . tokenUrl_ , {
445
- method : 'post' ,
446
- headers : headers ,
447
- payload : tokenPayload ,
448
- muteHttpExceptions : true
449
- } ) ;
450
- var token = this . getTokenFromResponse_ ( response ) ;
449
+ var token = this . fetchToken_ ( payload ) ;
451
450
this . saveToken_ ( token ) ;
452
451
return true ;
453
452
} ;
@@ -463,21 +462,18 @@ Service_.prototype.hasAccess = function() {
463
462
return this . lockable_ ( function ( ) {
464
463
var token = this . getToken ( ) ;
465
464
if ( ! token || this . isExpired_ ( token ) ) {
466
- if ( token && this . canRefresh_ ( token ) ) {
467
- try {
465
+ try {
466
+ if ( token && this . canRefresh_ ( token ) ) {
468
467
this . refresh ( ) ;
469
- } catch ( e ) {
470
- this . lastError_ = e ;
471
- return false ;
472
- }
473
- } else if ( this . privateKey_ ) {
474
- try {
468
+ } else if ( this . privateKey_ ) {
475
469
this . exchangeJwt_ ( ) ;
476
- } catch ( e ) {
477
- this . lastError_ = e ;
470
+ } else if ( this . grantType_ ) {
471
+ this . exchangeGrant_ ( ) ;
472
+ } else {
478
473
return false ;
479
474
}
480
- } else {
475
+ } catch ( e ) {
476
+ this . lastError_ = e ;
481
477
return false ;
482
478
}
483
479
}
@@ -525,6 +521,34 @@ Service_.prototype.getRedirectUri = function() {
525
521
return getRedirectUri ( this . scriptId_ ) ;
526
522
} ;
527
523
524
+
525
+ /**
526
+ * Fetches a new token from the OAuth server.
527
+ * @param {Object } payload The token request payload.
528
+ * @param {string } [optUrl] The URL of the token endpoint.
529
+ * @return {Object } The parsed token.
530
+ */
531
+ Service_ . prototype . fetchToken_ = function ( payload , optUrl ) {
532
+ // Use the configured token URL unless one is specified.
533
+ var url = optUrl || this . tokenUrl_ ;
534
+ var headers = {
535
+ 'Accept' : this . tokenFormat_
536
+ } ;
537
+ if ( this . tokenHeaders_ ) {
538
+ headers = extend_ ( headers , this . tokenHeaders_ ) ;
539
+ }
540
+ if ( this . tokenPayloadHandler_ ) {
541
+ tokenPayload = this . tokenPayloadHandler_ ( payload ) ;
542
+ }
543
+ var response = UrlFetchApp . fetch ( url , {
544
+ method : 'post' ,
545
+ headers : headers ,
546
+ payload : payload ,
547
+ muteHttpExceptions : true
548
+ } ) ;
549
+ return this . getTokenFromResponse_ ( response ) ;
550
+ } ;
551
+
528
552
/**
529
553
* Gets the token from a UrlFetchApp response.
530
554
* @param {UrlFetchApp.HTTPResponse } response The response object.
@@ -595,30 +619,13 @@ Service_.prototype.refresh = function() {
595
619
if ( ! token . refresh_token ) {
596
620
throw new Error ( 'Offline access is required.' ) ;
597
621
}
598
- var headers = {
599
- Accept : this . tokenFormat_
600
- } ;
601
- if ( this . tokenHeaders_ ) {
602
- headers = extend_ ( headers , this . tokenHeaders_ ) ;
603
- }
604
- var tokenPayload = {
622
+ var payload = {
605
623
refresh_token : token . refresh_token ,
606
624
client_id : this . clientId_ ,
607
625
client_secret : this . clientSecret_ ,
608
626
grant_type : 'refresh_token'
609
627
} ;
610
- if ( this . tokenPayloadHandler_ ) {
611
- tokenPayload = this . tokenPayloadHandler_ ( tokenPayload ) ;
612
- }
613
- // Use the refresh URL if specified, otherwise fallback to the token URL.
614
- var url = this . refreshUrl_ || this . tokenUrl_ ;
615
- var response = UrlFetchApp . fetch ( url , {
616
- method : 'post' ,
617
- headers : headers ,
618
- payload : tokenPayload ,
619
- muteHttpExceptions : true
620
- } ) ;
621
- var newToken = this . getTokenFromResponse_ ( response ) ;
628
+ var newToken = this . fetchToken_ ( payload , this . refreshUrl_ ) ;
622
629
if ( ! newToken . refresh_token ) {
623
630
newToken . refresh_token = token . refresh_token ;
624
631
}
@@ -706,22 +713,11 @@ Service_.prototype.exchangeJwt_ = function() {
706
713
'Token URL' : this . tokenUrl_
707
714
} ) ;
708
715
var jwt = this . createJwt_ ( ) ;
709
- var headers = {
710
- 'Accept' : this . tokenFormat_
716
+ var payload = {
717
+ assertion : jwt ,
718
+ grant_type : 'urn:ietf:params:oauth:grant-type:jwt-bearer'
711
719
} ;
712
- if ( this . tokenHeaders_ ) {
713
- headers = extend_ ( headers , this . tokenHeaders_ ) ;
714
- }
715
- var response = UrlFetchApp . fetch ( this . tokenUrl_ , {
716
- method : 'post' ,
717
- headers : headers ,
718
- payload : {
719
- assertion : jwt ,
720
- grant_type : 'urn:ietf:params:oauth:grant-type:jwt-bearer'
721
- } ,
722
- muteHttpExceptions : true
723
- } ) ;
724
- var token = this . getTokenFromResponse_ ( response ) ;
720
+ var token = this . fetchToken_ ( payload ) ;
725
721
this . saveToken_ ( token ) ;
726
722
} ;
727
723
@@ -783,6 +779,24 @@ Service_.prototype.lockable_ = function(func) {
783
779
return result ;
784
780
} ;
785
781
782
+ /**
783
+ * Obtain an access token using the custom grant type specified. Most often
784
+ * this will be "client_credentials", in which case make sure to also specify an
785
+ * Authorization header if required by your OAuth provider.
786
+ */
787
+ Service_ . prototype . exchangeGrant_ = function ( ) {
788
+ validate_ ( {
789
+ 'Grant Type' : this . grantType_ ,
790
+ 'Token URL' : this . tokenUrl_
791
+ } ) ;
792
+ var payload = {
793
+ grant_type : this . grantType_
794
+ } ;
795
+ payload = extend_ ( payload , this . params_ ) ;
796
+ var token = this . fetchToken_ ( payload ) ;
797
+ this . saveToken_ ( token ) ;
798
+ } ;
799
+
786
800
// Copyright 2017 Google Inc. All Rights Reserved.
787
801
//
788
802
// Licensed under the Apache License, Version 2.0 (the "License");
0 commit comments