1
1
/**
2
2
* @license
3
- * Copyright 2017 Google Inc.
3
+ * Copyright 2017 Google LLC
4
4
*
5
5
* Licensed under the Apache License, Version 2.0 (the "License");
6
6
* you may not use this file except in compliance with the License.
@@ -47,6 +47,8 @@ fireauth.StsTokenManager = function(rpcHandler) {
47
47
this . refreshToken_ = null ;
48
48
/** @private {?fireauth.IdToken} The STS ID token. */
49
49
this . accessToken_ = null ;
50
+ /** @private {number} The expiration time of the token in epoch millis. */
51
+ this . expiresAt_ = Date . now ( ) ;
50
52
} ;
51
53
52
54
@@ -73,13 +75,14 @@ fireauth.StsTokenManager.prototype.toPlainObject = function() {
73
75
* plain object provided using the RPC handler provided.
74
76
*/
75
77
fireauth . StsTokenManager . fromPlainObject = function ( rpcHandler , obj ) {
76
- var stsTokenManager = null ;
78
+ let stsTokenManager = null ;
77
79
if ( obj && obj [ 'apiKey' ] ) {
78
80
// These should be always equals and must be enforced in internal use.
79
81
goog . asserts . assert ( obj [ 'apiKey' ] == rpcHandler . getApiKey ( ) ) ;
80
82
stsTokenManager = new fireauth . StsTokenManager ( rpcHandler ) ;
81
83
stsTokenManager . setRefreshToken ( obj [ 'refreshToken' ] ) ;
82
84
stsTokenManager . setAccessToken ( obj [ 'accessToken' ] ) ;
85
+ stsTokenManager . setExpiresAt ( obj [ 'expirationTime' ] ) ;
83
86
}
84
87
return stsTokenManager ;
85
88
} ;
@@ -118,6 +121,30 @@ fireauth.StsTokenManager.prototype.setAccessToken = function(accessToken) {
118
121
this . accessToken_ = fireauth . IdToken . parse ( accessToken || '' ) ;
119
122
} ;
120
123
124
+ /**
125
+ * To account for client side clock skew, we try to set the expiration time
126
+ * using the local clock by adding the server TTL. If not provided, expiresAt
127
+ * will be set from the accessToken by taking the difference between the exp
128
+ * and iat fields.
129
+ *
130
+ * @param {number= } expiresIn The expiration TTL in seconds.
131
+ */
132
+ fireauth . StsTokenManager . prototype . setExpiresIn = function ( expiresIn ) {
133
+ expiresIn = typeof expiresIn !== 'undefined' ? expiresIn :
134
+ this . accessToken_ ? this . accessToken_ . getExpiresIn ( ) :
135
+ 0 ;
136
+ this . expiresAt_ = Date . now ( ) + expiresIn * 1000 ;
137
+ }
138
+
139
+ /**
140
+ * Allow setting expiresAt directly when we know the time is already in the
141
+ * local clock.
142
+ *
143
+ * @param {number } expiresAt The expiration time in epoch millis.
144
+ */
145
+ fireauth . StsTokenManager . prototype . setExpiresAt = function ( expiresAt ) {
146
+ this . expiresAt_ = expiresAt ;
147
+ }
121
148
122
149
/**
123
150
* @return {?string } The refresh token.
@@ -131,7 +158,7 @@ fireauth.StsTokenManager.prototype.getRefreshToken = function() {
131
158
* @return {number } The STS access token expiration time in milliseconds.
132
159
*/
133
160
fireauth . StsTokenManager . prototype . getExpirationTime = function ( ) {
134
- return ( this . accessToken_ && this . accessToken_ . getExp ( ) * 1000 ) || 0 ;
161
+ return this . expiresAt_ ;
135
162
} ;
136
163
137
164
@@ -148,7 +175,7 @@ fireauth.StsTokenManager.TOKEN_REFRESH_BUFFER = 30 * 1000;
148
175
* @private
149
176
*/
150
177
fireauth . StsTokenManager . prototype . isExpired_ = function ( ) {
151
- return goog . now ( ) >
178
+ return Date . now ( ) >
152
179
this . getExpirationTime ( ) - fireauth . StsTokenManager . TOKEN_REFRESH_BUFFER ;
153
180
} ;
154
181
@@ -161,11 +188,14 @@ fireauth.StsTokenManager.prototype.isExpired_ = function() {
161
188
* @return {string } The STS access token.
162
189
*/
163
190
fireauth . StsTokenManager . prototype . parseServerResponse = function ( response ) {
164
- var idToken = response [ fireauth . RpcHandler . AuthServerField . ID_TOKEN ] ;
165
- var refreshToken =
166
- response [ fireauth . RpcHandler . AuthServerField . REFRESH_TOKEN ] ;
191
+ const idToken = response [ fireauth . RpcHandler . AuthServerField . ID_TOKEN ] ;
167
192
this . setAccessToken ( idToken ) ;
168
- this . setRefreshToken ( refreshToken ) ;
193
+ this . setRefreshToken (
194
+ response [ fireauth . RpcHandler . AuthServerField . REFRESH_TOKEN ] ) ;
195
+ // Not all IDP server responses come with expiresIn, some like MFA omit it.
196
+ const expiresIn = response [ fireauth . RpcHandler . AuthServerField . EXPIRES_IN ] ;
197
+ this . setExpiresIn (
198
+ typeof expiresIn !== 'undefined' ? Number ( expiresIn ) : undefined ) ;
169
199
return idToken ;
170
200
} ;
171
201
@@ -175,7 +205,7 @@ fireauth.StsTokenManager.prototype.parseServerResponse = function(response) {
175
205
* @return {!Object }
176
206
*/
177
207
fireauth . StsTokenManager . prototype . toServerResponse = function ( ) {
178
- var stsTokenManagerResponse = { } ;
208
+ const stsTokenManagerResponse = { } ;
179
209
stsTokenManagerResponse [ fireauth . RpcHandler . AuthServerField . ID_TOKEN ] =
180
210
this . accessToken_ && this . accessToken_ . toString ( ) ;
181
211
// Refresh token could be expired.
@@ -192,6 +222,7 @@ fireauth.StsTokenManager.prototype.toServerResponse = function() {
192
222
fireauth . StsTokenManager . prototype . copy = function ( tokenManagerToCopy ) {
193
223
this . accessToken_ = tokenManagerToCopy . accessToken_ ;
194
224
this . refreshToken_ = tokenManagerToCopy . refreshToken_ ;
225
+ this . expiresAt_ = tokenManagerToCopy . expiresAt_ ;
195
226
} ;
196
227
197
228
@@ -201,7 +232,7 @@ fireauth.StsTokenManager.prototype.copy = function(tokenManagerToCopy) {
201
232
* @private
202
233
*/
203
234
fireauth . StsTokenManager . prototype . exchangeRefreshToken_ = function ( ) {
204
- var data = {
235
+ const data = {
205
236
'grant_type' : 'refresh_token' ,
206
237
'refresh_token' : this . refreshToken_
207
238
} ;
@@ -216,27 +247,32 @@ fireauth.StsTokenManager.prototype.exchangeRefreshToken_ = function() {
216
247
* @private
217
248
*/
218
249
fireauth . StsTokenManager . prototype . requestToken_ = function ( data ) {
219
- var self = this ;
220
250
// Send RPC request to STS token endpoint.
221
- return this . rpcHandler_ . requestStsToken ( data ) . then ( function ( resp ) {
222
- var response = /** @type {!fireauth.StsTokenManager.ResponseData } */ ( resp ) ;
223
- self . accessToken_ = fireauth . IdToken . parse (
224
- response [ fireauth . RpcHandler . StsServerField . ACCESS_TOKEN ] ) ;
225
- self . refreshToken_ =
226
- response [ fireauth . RpcHandler . StsServerField . REFRESH_TOKEN ] ;
227
- return /** @type {!fireauth.StsTokenManager.Response } */ ( {
228
- 'accessToken' : self . accessToken_ . toString ( ) ,
229
- 'refreshToken' : self . refreshToken_
230
- } ) ;
231
- } ) . thenCatch ( function ( error ) {
232
- // Refresh token expired or user deleted. In this case, reset refresh token
233
- // to prevent sending the request again to the STS server unless
234
- // the token is manually updated, perhaps via successful reauthentication.
235
- if ( error [ 'code' ] == 'auth/user-token-expired' ) {
236
- self . refreshToken_ = null ;
237
- }
238
- throw error ;
239
- } ) ;
251
+ return this . rpcHandler_ . requestStsToken ( data )
252
+ . then ( ( resp ) => {
253
+ const response =
254
+ /** @type {!fireauth.StsTokenManager.ResponseData } */ ( resp ) ;
255
+ this . accessToken_ = fireauth . IdToken . parse (
256
+ response [ fireauth . RpcHandler . StsServerField . ACCESS_TOKEN ] ) ;
257
+ this . refreshToken_ =
258
+ response [ fireauth . RpcHandler . StsServerField . REFRESH_TOKEN ] ;
259
+ this . setExpiresIn (
260
+ response [ fireauth . RpcHandler . StsServerField . EXPIRES_IN ] ) ;
261
+ return /** @type {!fireauth.StsTokenManager.Response } */ ( {
262
+ 'accessToken' : this . accessToken_ . toString ( ) ,
263
+ 'refreshToken' : this . refreshToken_
264
+ } ) ;
265
+ } )
266
+ . thenCatch ( ( error ) => {
267
+ // Refresh token expired or user deleted. In this case, reset refresh
268
+ // token to prevent sending the request again to the STS server unless
269
+ // the token is manually updated, perhaps via successful
270
+ // reauthentication.
271
+ if ( error [ 'code' ] == 'auth/user-token-expired' ) {
272
+ this . refreshToken_ = null ;
273
+ }
274
+ throw error ;
275
+ } ) ;
240
276
} ;
241
277
242
278
@@ -251,12 +287,11 @@ fireauth.StsTokenManager.prototype.isRefreshTokenExpired = function() {
251
287
* Otherwise the existing ID token or refresh token is exchanged for a new one.
252
288
* If there is no user signed in, returns null.
253
289
*
254
- * @param {boolean= } opt_forceRefresh Whether to force refresh token exchange.
290
+ * @param {boolean= } forceRefresh Whether to force refresh token exchange.
255
291
* @return {!goog.Promise<?fireauth.StsTokenManager.Response> }
256
292
*/
257
- fireauth . StsTokenManager . prototype . getToken = function ( opt_forceRefresh ) {
258
- var self = this ;
259
- var forceRefresh = ! ! opt_forceRefresh ;
293
+ fireauth . StsTokenManager . prototype . getToken = function ( forceRefresh ) {
294
+ forceRefresh = ! ! forceRefresh ;
260
295
// Refresh token is expired.
261
296
if ( this . isRefreshTokenExpired ( ) ) {
262
297
return goog . Promise . reject (
@@ -265,8 +300,8 @@ fireauth.StsTokenManager.prototype.getToken = function(opt_forceRefresh) {
265
300
if ( ! forceRefresh && this . accessToken_ && ! this . isExpired_ ( ) ) {
266
301
// Cached STS access token not expired, return it.
267
302
return /** @type {!goog.Promise } */ ( goog . Promise . resolve ( {
268
- 'accessToken' : self . accessToken_ . toString ( ) ,
269
- 'refreshToken' : self . refreshToken_
303
+ 'accessToken' : this . accessToken_ . toString ( ) ,
304
+ 'refreshToken' : this . refreshToken_
270
305
} ) ) ;
271
306
} else if ( this . refreshToken_ ) {
272
307
// Expired but refresh token available, exchange refresh token for STS
0 commit comments