@@ -21,8 +21,12 @@ import {
2121 AppCheckTokenResult ,
2222 AppCheckTokenListener
2323} from '@firebase/app-check-interop-types' ;
24- import { AppCheckToken } from '@firebase/app-check-types' ;
25- import { getDebugState , getState , setState } from './state' ;
24+ import {
25+ AppCheckTokenInternal ,
26+ getDebugState ,
27+ getState ,
28+ setState
29+ } from './state' ;
2630import { TOKEN_REFRESH_TIME } from './constants' ;
2731import { Refresher } from './proactive-refresh' ;
2832import { ensureActivated } from './util' ;
@@ -33,7 +37,7 @@ import {
3337} from './client' ;
3438import { writeTokenToStorage , readTokenFromStorage } from './storage' ;
3539import { getDebugToken , isDebugMode } from './debug' ;
36- import { base64 } from '@firebase/util' ;
40+ import { base64 , issuedAtTime } from '@firebase/util' ;
3741import { ERROR_FACTORY , AppCheckError } from './errors' ;
3842import { logger } from './logger' ;
3943import { Provider } from '@firebase/component' ;
@@ -72,7 +76,7 @@ export async function getToken(
7276 * return the debug token directly
7377 */
7478 if ( isDebugMode ( ) ) {
75- const tokenFromDebugExchange : AppCheckToken = await exchangeToken (
79+ const tokenFromDebugExchange : AppCheckTokenInternal = await exchangeToken (
7680 getExchangeDebugTokenRequest ( app , await getDebugToken ( ) ) ,
7781 platformLoggerProvider
7882 ) ;
@@ -81,7 +85,7 @@ export async function getToken(
8185
8286 const state = getState ( app ) ;
8387
84- let token : AppCheckToken | undefined = state . token ;
88+ let token : AppCheckTokenInternal | undefined = state . token ;
8589 let error : Error | undefined = undefined ;
8690
8791 /**
@@ -111,7 +115,20 @@ export async function getToken(
111115 */
112116 try {
113117 if ( state . customProvider ) {
114- token = await state . customProvider . getToken ( ) ;
118+ const customToken = await state . customProvider . getToken ( ) ;
119+ // Try to extract IAT from custom token, in case this token is not
120+ // being newly issued. JWT timestamps are in seconds since epoch.
121+ const issuedAtTimeSeconds = issuedAtTime ( customToken . token ) ;
122+ // Very basic validation, use current timestamp as IAT if JWT
123+ // has no `iat` field or value is out of bounds.
124+ const issuedAtTimeMillis =
125+ issuedAtTimeSeconds !== null &&
126+ issuedAtTimeSeconds < Date . now ( ) &&
127+ issuedAtTimeSeconds > 0
128+ ? issuedAtTimeSeconds * 1000
129+ : Date . now ( ) ;
130+
131+ token = { ...customToken , issuedAtTimeMillis } ;
115132 } else {
116133 const attestedClaimsToken = await getReCAPTCHAToken ( app ) . catch ( _e => {
117134 // reCaptcha.execute() throws null which is not very descriptive.
@@ -183,7 +200,12 @@ export function addTokenListener(
183200 newState . tokenRefresher = tokenRefresher ;
184201 }
185202
186- if ( ! newState . tokenRefresher . isRunning ( ) ) {
203+ // Create the refresher but don't start it if `isTokenAutoRefreshEnabled`
204+ // is not true.
205+ if (
206+ ! newState . tokenRefresher . isRunning ( ) &&
207+ state . isTokenAutoRefreshEnabled === true
208+ ) {
187209 newState . tokenRefresher . start ( ) ;
188210 }
189211
@@ -253,12 +275,20 @@ function createTokenRefresher(
253275 const state = getState ( app ) ;
254276
255277 if ( state . token ) {
256- return Math . max (
257- 0 ,
258- state . token . expireTimeMillis -
259- Date . now ( ) -
260- TOKEN_REFRESH_TIME . OFFSET_DURATION
278+ // issuedAtTime + (50% * total TTL) + 5 minutes
279+ let nextRefreshTimeMillis =
280+ state . token . issuedAtTimeMillis +
281+ ( state . token . expireTimeMillis - state . token . issuedAtTimeMillis ) *
282+ 0.5 +
283+ 5 * 60 * 1000 ;
284+ // Do not allow refresh time to be past (expireTime - 5 minutes)
285+ const latestAllowableRefresh =
286+ state . token . expireTimeMillis - 5 * 60 * 1000 ;
287+ nextRefreshTimeMillis = Math . min (
288+ nextRefreshTimeMillis ,
289+ latestAllowableRefresh
261290 ) ;
291+ return Math . max ( 0 , nextRefreshTimeMillis - Date . now ( ) ) ;
262292 } else {
263293 return 0 ;
264294 }
@@ -283,7 +313,7 @@ function notifyTokenListeners(
283313 }
284314}
285315
286- function isValid ( token : AppCheckToken ) : boolean {
316+ function isValid ( token : AppCheckTokenInternal ) : boolean {
287317 return token . expireTimeMillis - Date . now ( ) > 0 ;
288318}
289319
0 commit comments