@@ -44,6 +44,20 @@ var Service_ = function(serviceName) {
4444 */ 
4545Service_ . EXPIRATION_BUFFER_SECONDS_  =  60 ; 
4646
47+ /** 
48+  * The number of seconds that a token should remain in the cache. 
49+  * @type  {number } 
50+  * @private  
51+  */ 
52+ Service_ . CACHE_EXPIRATION_SECONDS_  =  6  *  60  *  60 ; 
53+ 
54+ /** 
55+  * The number of seconds that a token should remain in the cache. 
56+  * @type  {number } 
57+  * @private  
58+  */ 
59+ Service_ . LOCK_EXPIRATION_MILLISECONDS_  =  30  *  1000 ; 
60+ 
4761/** 
4862 * Sets the service's authorization base URL (required). For Google services this URL should be 
4963 * https://accounts.google.com/o/oauth2/auth. 
@@ -173,6 +187,18 @@ Service_.prototype.setCache = function(cache) {
173187 return  this ; 
174188} ; 
175189
190+ /** 
191+  * Sets the lock to use when checking and refreshing credentials (optional). Using a lock will 
192+  * ensure that only one execution will be able to access the stored credentials at a time. This can 
193+  * prevent race conditions that arise when two executions attempt to refresh an expired token. 
194+  * @param  {LockService.Lock } cache The lock to use when accessing credentials. 
195+  * @return  {Service_ } This service, for chaining. 
196+  */ 
197+ Service_ . prototype . setLock  =  function ( lock )  { 
198+  this . lock_  =  lock ; 
199+  return  this ; 
200+ } ; 
201+ 
176202/** 
177203 * Sets the scope or scopes to request during the authorization flow (optional). If the scope value 
178204 * is an array it will be joined using the separator before being sent to the server, which is 
@@ -329,27 +355,32 @@ Service_.prototype.handleCallback = function(callbackRequest) {
329355 * @return  {boolean } true if the user has access to the service, false otherwise. 
330356 */ 
331357Service_ . prototype . hasAccess  =  function ( )  { 
358+  if  ( this . lock_ )  { 
359+  this . lock_ . waitLock ( Service_ . LOCK_EXPIRATION_MILLISECONDS_ ) ; 
360+  } 
361+  var  result  =  true ; 
332362 var  token  =  this . getToken ( ) ; 
333363 if  ( ! token  ||  this . isExpired_ ( token ) )  { 
334364 if  ( token  &&  token . refresh_token )  { 
335365 try  { 
336366 this . refresh ( ) ; 
337367 }  catch  ( e )  { 
338368 this . lastError_  =  e ; 
339-  return  false ; 
369+  result   =  false ; 
340370 } 
341371 }  else  if  ( this . privateKey_ )  { 
342372 try  { 
343373 this . exchangeJwt_ ( ) ; 
344374 }  catch  ( e )  { 
345375 this . lastError_  =  e ; 
346-  return  false ; 
376+  result   =  false ; 
347377 } 
348-  }  else  { 
349-  return  false ; 
350378 } 
351379 } 
352-  return  true ; 
380+  if  ( this . lock_ )  { 
381+  this . lock_ . releaseLock ( ) ; 
382+  } 
383+  return  result ; 
353384} ; 
354385
355386/** 
@@ -458,6 +489,9 @@ Service_.prototype.parseToken_ = function(content) {
458489 * requested when the token was authorized. 
459490 */ 
460491Service_ . prototype . refresh  =  function ( )  { 
492+  if  ( this . lock_ )  { 
493+  this . lock_ . waitLock ( Service_ . LOCK_EXPIRATION_MILLISECONDS_ ) ; 
494+  } 
461495 validate_ ( { 
462496 'Client ID' : this . clientId_ , 
463497 'Client Secret' : this . clientSecret_ , 
@@ -494,6 +528,9 @@ Service_.prototype.refresh = function() {
494528 newToken . refresh_token  =  token . refresh_token ; 
495529 } 
496530 this . saveToken_ ( newToken ) ; 
531+  if  ( this . lock_ )  { 
532+  this . lock_ . releaseLock ( ) ; 
533+  } 
497534} ; 
498535
499536/** 
@@ -509,7 +546,7 @@ Service_.prototype.saveToken_ = function(token) {
509546 var  value  =  JSON . stringify ( token ) ; 
510547 this . propertyStore_ . setProperty ( key ,  value ) ; 
511548 if  ( this . cache_ )  { 
512-  this . cache_ . put ( key ,  value ,  21600 ) ; 
549+  this . cache_ . put ( key ,  value ,  Service_ . CACHE_EXPIRATION_SECONDS_ ) ; 
513550 } 
514551 this . token_  =  token ; 
515552} ; 
@@ -541,7 +578,7 @@ Service_.prototype.getToken = function() {
541578 // Check PropertiesService store. 
542579 if  ( ( token  =  this . propertyStore_ . getProperty ( key ) ) )  { 
543580 if  ( this . cache_ )  { 
544-  this . cache_ . put ( key ,  token ,  21600 ) ; 
581+  this . cache_ . put ( key ,  token ,  Service_ . CACHE_EXPIRATION_SECONDS_ ) ; 
545582 } 
546583 token  =  JSON . parse ( token ) ; 
547584 this . token_  =  token ; 
0 commit comments