@@ -672,7 +672,7 @@ Service_.prototype.refresh = function() {
672672 * Custom values associated with the service can be stored here as well.
673673 * The key <code>null</code> is used to to store the token and should not
674674 * be used.
675- * @return {Storage } The service's storage.
675+ * @return {Storage_ } The service's storage.
676676 */
677677Service_ . prototype . getStorage = function ( ) {
678678 if ( ! this . storage_ ) {
@@ -703,20 +703,35 @@ Service_.prototype.getToken = function(optSkipMemoryCheck) {
703703} ;
704704
705705/**
706- * Determines if a retrieved token is still valid.
706+ * Determines if a retrieved token is still valid. This will return false if
707+ * either the authorization token or the ID token has expired.
707708 * @param {Object } token The token to validate.
708709 * @return {boolean } True if it has expired, false otherwise.
709710 * @private
710711 */
711712Service_ . prototype . isExpired_ = function ( token ) {
713+ var expired = false ;
714+ var now = getTimeInSeconds_ ( new Date ( ) ) ;
715+
716+ // Check the authorization token's expiration.
712717 var expiresIn = token . expires_in_sec || token . expires_in || token . expires ;
713- if ( ! expiresIn ) {
714- return false ;
715- } else {
718+ if ( expiresIn ) {
716719 var expiresTime = token . granted_time + Number ( expiresIn ) ;
717- var now = getTimeInSeconds_ ( new Date ( ) ) ;
718- return expiresTime - now < Service_ . EXPIRATION_BUFFER_SECONDS_ ;
720+ if ( expiresTime - now < Service_ . EXPIRATION_BUFFER_SECONDS_ ) {
721+ expired = true ;
722+ }
719723 }
724+
725+ // Check the ID token's expiration, if it exists.
726+ if ( token . id_token ) {
727+ var payload = decodeJwt_ ( token . id_token ) ;
728+ if ( payload . exp &&
729+ payload . exp - now < Service_ . EXPIRATION_BUFFER_SECONDS_ ) {
730+ expired = true ;
731+ }
732+ }
733+
734+ return expired ;
720735} ;
721736
722737/**
@@ -767,10 +782,6 @@ Service_.prototype.createJwt_ = function() {
767782 'Token URL' : this . tokenUrl_ ,
768783 'Issuer or Client ID' : this . issuer_ || this . clientId_
769784 } ) ;
770- var header = {
771- alg : 'RS256' ,
772- typ : 'JWT'
773- } ;
774785 var now = new Date ( ) ;
775786 var expires = new Date ( now . getTime ( ) ) ;
776787 expires . setMinutes ( expires . getMinutes ( ) + this . expirationMinutes_ ) ;
@@ -792,12 +803,7 @@ Service_.prototype.createJwt_ = function() {
792803 claimSet [ key ] = additionalClaims [ key ] ;
793804 } ) ;
794805 }
795- var toSign = Utilities . base64EncodeWebSafe ( JSON . stringify ( header ) ) + '.' +
796- Utilities . base64EncodeWebSafe ( JSON . stringify ( claimSet ) ) ;
797- var signatureBytes =
798- Utilities . computeRsaSha256Signature ( toSign , this . privateKey_ ) ;
799- var signature = Utilities . base64EncodeWebSafe ( signatureBytes ) ;
800- return toSign + '.' + signature ;
806+ return encodeJwt_ ( claimSet , this . privateKey_ ) ;
801807} ;
802808
803809/**
@@ -1001,7 +1007,7 @@ Storage_.prototype.reset = function() {
10011007
10021008/**
10031009 * Removes a stored value.
1004- * @param {string } key The key.
1010+ * @param {string } prefixedKey The key.
10051011 */
10061012Storage_ . prototype . removeValueWithPrefixedKey_ = function ( prefixedKey ) {
10071013 if ( this . properties_ ) {
@@ -1111,7 +1117,7 @@ function extend_(destination, source) {
11111117 * Gets a copy of an object with all the keys converted to lower-case strings.
11121118 *
11131119 * @param {Object } obj The object to copy.
1114- * @return {Object } a shallow copy of the object with all lower-case keys.
1120+ * @return {Object } A shallow copy of the object with all lower-case keys.
11151121 */
11161122function toLowerCaseKeys_ ( obj ) {
11171123 if ( obj === null || typeof obj !== 'object' ) {
@@ -1125,6 +1131,40 @@ function toLowerCaseKeys_(obj) {
11251131 } , { } ) ;
11261132}
11271133
1134+ /* exported encodeJwt_ */
1135+ /**
1136+ * Encodes and signs a JWT.
1137+ *
1138+ * @param {Object } payload The JWT payload.
1139+ * @param {string } key The key to use when generating the signature.
1140+ * @return {string } The encoded and signed JWT.
1141+ */
1142+ function encodeJwt_ ( payload , key ) {
1143+ var header = {
1144+ alg : 'RS256' ,
1145+ typ : 'JWT'
1146+ } ;
1147+ var toSign = Utilities . base64EncodeWebSafe ( JSON . stringify ( header ) ) + '.' +
1148+ Utilities . base64EncodeWebSafe ( JSON . stringify ( payload ) ) ;
1149+ var signatureBytes =
1150+ Utilities . computeRsaSha256Signature ( toSign , key ) ;
1151+ var signature = Utilities . base64EncodeWebSafe ( signatureBytes ) ;
1152+ return toSign + '.' + signature ;
1153+ }
1154+
1155+ /* exported decodeJwt_ */
1156+ /**
1157+ * Decodes and returns the parts of the JWT. The signature is not verified.
1158+ *
1159+ * @param {string } jwt The JWT to decode.
1160+ * @return {Object } The decoded payload.
1161+ */
1162+ function decodeJwt_ ( jwt ) {
1163+ var payload = jwt . split ( '.' ) [ 1 ] ;
1164+ var blob = Utilities . newBlob ( Utilities . base64DecodeWebSafe ( payload ) ) ;
1165+ return JSON . parse ( blob . getDataAsString ( ) ) ;
1166+ }
1167+
11281168 /****** code end *********/
11291169 ; (
11301170function copy ( src , target , obj ) {
0 commit comments