Skip to content

Commit 3e7400f

Browse files
committed
Add TTL formula
1 parent f5de2a7 commit 3e7400f

File tree

8 files changed

+47
-29
lines changed

8 files changed

+47
-29
lines changed

packages/app-check/src/client.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ describe('client', () => {
7474

7575
expect(response).to.deep.equal({
7676
token: 'fake-appcheck-token',
77-
expireTimeMillis: 3600
77+
expireTimeMillis: 3600,
78+
issuedAtTimeMillis: 0
7879
});
7980
});
8081

packages/app-check/src/client.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ import {
2222
} from './constants';
2323
import { FirebaseApp } from '@firebase/app-types';
2424
import { ERROR_FACTORY, AppCheckError } from './errors';
25-
import { AppCheckToken } from '@firebase/app-check-types';
2625
import { Provider } from '@firebase/component';
26+
import { AppCheckTokenInternal } from './state';
2727

2828
/**
2929
* Response JSON returned from AppCheck server endpoint.
@@ -42,7 +42,7 @@ interface AppCheckRequest {
4242
export async function exchangeToken(
4343
{ url, body }: AppCheckRequest,
4444
platformLoggerProvider: Provider<'platform-logger'>
45-
): Promise<AppCheckToken> {
45+
): Promise<AppCheckTokenInternal> {
4646
const headers: HeadersInit = {
4747
'Content-Type': 'application/json'
4848
};
@@ -95,9 +95,11 @@ export async function exchangeToken(
9595
}
9696
const timeToLiveAsNumber = Number(match[1]) * 1000;
9797

98+
const now = Date.now();
9899
return {
99100
token: responseBody.attestationToken,
100-
expireTimeMillis: Date.now() + timeToLiveAsNumber
101+
expireTimeMillis: now + timeToLiveAsNumber,
102+
issuedAtTimeMillis: now
101103
};
102104
}
103105

packages/app-check/src/indexeddb.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { AppCheckToken } from '@firebase/app-check-types';
1918
import { FirebaseApp } from '@firebase/app-types';
2019
import { ERROR_FACTORY, AppCheckError } from './errors';
20+
import { AppCheckTokenInternal } from './state';
2121
const DB_NAME = 'firebase-app-check-database';
2222
const DB_VERSION = 1;
2323
const STORE_NAME = 'firebase-app-check-store';
@@ -74,13 +74,13 @@ function getDBPromise(): Promise<IDBDatabase> {
7474

7575
export function readTokenFromIndexedDB(
7676
app: FirebaseApp
77-
): Promise<AppCheckToken | undefined> {
78-
return read(computeKey(app)) as Promise<AppCheckToken | undefined>;
77+
): Promise<AppCheckTokenInternal | undefined> {
78+
return read(computeKey(app)) as Promise<AppCheckTokenInternal | undefined>;
7979
}
8080

8181
export function writeTokenToIndexedDB(
8282
app: FirebaseApp,
83-
token: AppCheckToken
83+
token: AppCheckTokenInternal
8484
): Promise<void> {
8585
return write(computeKey(app), token);
8686
}

packages/app-check/src/internal-api.test.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,14 @@ describe('internal api', () => {
5959
const fakeRecaptchaToken = 'fake-recaptcha-token';
6060
const fakeRecaptchaAppCheckToken = {
6161
token: 'fake-recaptcha-app-check-token',
62-
expireTimeMillis: 123
62+
expireTimeMillis: 123,
63+
issuedAtTimeMillis: 0
6364
};
6465

6566
const fakeCachedAppCheckToken = {
6667
token: 'fake-cached-app-check-token',
67-
expireTimeMillis: 123
68+
expireTimeMillis: 123,
69+
issuedAtTimeMillis: 0
6870
};
6971

7072
it('uses customTokenProvider to get an AppCheck token', async () => {
@@ -318,7 +320,8 @@ describe('internal api', () => {
318320
...getState(app),
319321
token: {
320322
token: `fake-memory-app-check-token`,
321-
expireTimeMillis: 123
323+
expireTimeMillis: 123,
324+
issuedAtTimeMillis: 0
322325
}
323326
});
324327

@@ -331,7 +334,8 @@ describe('internal api', () => {
331334
stub(storage, 'readTokenFromStorage').returns(
332335
Promise.resolve({
333336
token: `fake-cached-app-check-token`,
334-
expireTimeMillis: 123
337+
expireTimeMillis: 123,
338+
issuedAtTimeMillis: 0
335339
})
336340
);
337341

packages/app-check/src/internal-api.ts

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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';
2630
import { TOKEN_REFRESH_TIME } from './constants';
2731
import { Refresher } from './proactive-refresh';
2832
import { ensureActivated } from './util';
@@ -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,8 @@ 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+
token = { ...customToken, issuedAtTimeMillis: Date.now() };
115120
} else {
116121
const attestedClaimsToken = await getReCAPTCHAToken(app).catch(_e => {
117122
// reCaptcha.execute() throws null which is not very descriptive.
@@ -258,12 +263,13 @@ function createTokenRefresher(
258263
const state = getState(app);
259264

260265
if (state.token) {
261-
return Math.max(
262-
0,
263-
state.token.expireTimeMillis -
264-
Date.now() -
265-
TOKEN_REFRESH_TIME.OFFSET_DURATION
266-
);
266+
// issuedAtTime + (50% * total TTL) + 5 minutes
267+
const nextRefreshTimeMillis =
268+
state.token.issuedAtTimeMillis +
269+
(state.token.expireTimeMillis - state.token.issuedAtTimeMillis) *
270+
0.5 +
271+
5 * 60 * 1000;
272+
return Math.max(0, nextRefreshTimeMillis - Date.now());
267273
} else {
268274
return 0;
269275
}
@@ -288,7 +294,7 @@ function notifyTokenListeners(
288294
}
289295
}
290296

291-
function isValid(token: AppCheckToken): boolean {
297+
function isValid(token: AppCheckTokenInternal): boolean {
292298
return token.expireTimeMillis - Date.now() > 0;
293299
}
294300

packages/app-check/src/state.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,16 @@ import { AppCheckTokenListener } from '@firebase/app-check-interop-types';
2121
import { Refresher } from './proactive-refresh';
2222
import { Deferred } from '@firebase/util';
2323
import { GreCAPTCHA } from './recaptcha';
24+
25+
export interface AppCheckTokenInternal extends AppCheckToken {
26+
issuedAtTimeMillis: number;
27+
}
2428
export interface AppCheckState {
2529
activated: boolean;
2630
tokenListeners: AppCheckTokenListener[];
2731
customProvider?: AppCheckProvider;
2832
siteKey?: string;
29-
token?: AppCheckToken;
33+
token?: AppCheckTokenInternal;
3034
tokenRefresher?: Refresher;
3135
reCAPTCHAState?: ReCAPTCHAState;
3236
isTokenAutoRefreshEnabled?: boolean;

packages/app-check/src/storage.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ describe('Storage', () => {
2828
const app = getFakeApp();
2929
const fakeToken = {
3030
token: 'fake-app-check-token',
31-
expireTimeMillis: 345
31+
expireTimeMillis: 345,
32+
issuedAtTimeMillis: 0
3233
};
3334

3435
it('sets and gets appCheck token to indexeddb', async () => {

packages/app-check/src/storage.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { AppCheckToken } from '@firebase/app-check-types';
1918
import { uuidv4 } from './util';
2019
import { FirebaseApp } from '@firebase/app-types';
2120
import { isIndexedDBAvailable } from '@firebase/util';
@@ -26,13 +25,14 @@ import {
2625
writeTokenToIndexedDB
2726
} from './indexeddb';
2827
import { logger } from './logger';
28+
import { AppCheckTokenInternal } from './state';
2929

3030
/**
3131
* Always resolves. In case of an error reading from indexeddb, resolve with undefined
3232
*/
3333
export async function readTokenFromStorage(
3434
app: FirebaseApp
35-
): Promise<AppCheckToken | undefined> {
35+
): Promise<AppCheckTokenInternal | undefined> {
3636
if (isIndexedDBAvailable()) {
3737
let token = undefined;
3838
try {
@@ -52,7 +52,7 @@ export async function readTokenFromStorage(
5252
*/
5353
export function writeTokenToStorage(
5454
app: FirebaseApp,
55-
token: AppCheckToken
55+
token: AppCheckTokenInternal
5656
): Promise<void> {
5757
if (isIndexedDBAvailable()) {
5858
return writeTokenToIndexedDB(app, token).catch(e => {

0 commit comments

Comments
 (0)