Skip to content

Commit 80bf9e2

Browse files
committed
Add TTL formula
1 parent d881748 commit 80bf9e2

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
@@ -69,7 +69,8 @@ describe('client', () => {
6969

7070
expect(response).to.deep.equal({
7171
token: 'fake-appcheck-token',
72-
expireTimeMillis: 3600
72+
expireTimeMillis: 3600,
73+
issuedAtTimeMillis: 0
7374
});
7475
});
7576

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 { version } from '../package.json';
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,
4444
body
45-
}: AppCheckRequest): Promise<AppCheckToken> {
45+
}: AppCheckRequest): Promise<AppCheckTokenInternal> {
4646
const options = {
4747
method: 'POST',
4848
body: JSON.stringify(body),
@@ -89,9 +89,11 @@ export async function exchangeToken({
8989
}
9090
const timeToLiveAsNumber = Number(match[1]) * 1000;
9191

92+
const now = Date.now();
9293
return {
9394
token: responseBody.attestationToken,
94-
expireTimeMillis: Date.now() + timeToLiveAsNumber
95+
expireTimeMillis: now + timeToLiveAsNumber,
96+
issuedAtTimeMillis: now
9597
};
9698
}
9799

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
@@ -56,12 +56,14 @@ describe('internal api', () => {
5656
const fakeRecaptchaToken = 'fake-recaptcha-token';
5757
const fakeRecaptchaAppCheckToken = {
5858
token: 'fake-recaptcha-app-check-token',
59-
expireTimeMillis: 123
59+
expireTimeMillis: 123,
60+
issuedAtTimeMillis: 0
6061
};
6162

6263
const fakeCachedAppCheckToken = {
6364
token: 'fake-cached-app-check-token',
64-
expireTimeMillis: 123
65+
expireTimeMillis: 123,
66+
issuedAtTimeMillis: 0
6567
};
6668

6769
it('uses customTokenProvider to get an AppCheck token', async () => {
@@ -313,7 +315,8 @@ describe('internal api', () => {
313315
...getState(app),
314316
token: {
315317
token: `fake-memory-app-check-token`,
316-
expireTimeMillis: 123
318+
expireTimeMillis: 123,
319+
issuedAtTimeMillis: 0
317320
}
318321
});
319322

@@ -326,7 +329,8 @@ describe('internal api', () => {
326329
stub(storage, 'readTokenFromStorage').returns(
327330
Promise.resolve({
328331
token: `fake-cached-app-check-token`,
329-
expireTimeMillis: 123
332+
expireTimeMillis: 123,
333+
issuedAtTimeMillis: 0
330334
})
331335
);
332336

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';
@@ -70,15 +74,15 @@ export async function getToken(
7074
* return the debug token directly
7175
*/
7276
if (isDebugMode()) {
73-
const tokenFromDebugExchange: AppCheckToken = await exchangeToken(
77+
const tokenFromDebugExchange: AppCheckTokenInternal = await exchangeToken(
7478
getExchangeDebugTokenRequest(app, await getDebugToken())
7579
);
7680
return { token: tokenFromDebugExchange.token };
7781
}
7882

7983
const state = getState(app);
8084

81-
let token: AppCheckToken | undefined = state.token;
85+
let token: AppCheckTokenInternal | undefined = state.token;
8286
let error: Error | undefined = undefined;
8387

8488
/**
@@ -108,7 +112,8 @@ export async function getToken(
108112
*/
109113
try {
110114
if (state.customProvider) {
111-
token = await state.customProvider.getToken();
115+
const customToken = await state.customProvider.getToken();
116+
token = { ...customToken, issuedAtTimeMillis: Date.now() };
112117
} else {
113118
const attestedClaimsToken = await getReCAPTCHAToken(app).catch(_e => {
114119
// reCaptcha.execute() throws null which is not very descriptive.
@@ -250,12 +255,13 @@ function createTokenRefresher(app: FirebaseApp): Refresher {
250255
const state = getState(app);
251256

252257
if (state.token) {
253-
return Math.max(
254-
0,
255-
state.token.expireTimeMillis -
256-
Date.now() -
257-
TOKEN_REFRESH_TIME.OFFSET_DURATION
258-
);
258+
// issuedAtTime + (50% * total TTL) + 5 minutes
259+
const nextRefreshTimeMillis =
260+
state.token.issuedAtTimeMillis +
261+
(state.token.expireTimeMillis - state.token.issuedAtTimeMillis) *
262+
0.5 +
263+
5 * 60 * 1000;
264+
return Math.max(0, nextRefreshTimeMillis - Date.now());
259265
} else {
260266
return 0;
261267
}
@@ -280,7 +286,7 @@ function notifyTokenListeners(
280286
}
281287
}
282288

283-
function isValid(token: AppCheckToken): boolean {
289+
function isValid(token: AppCheckTokenInternal): boolean {
284290
return token.expireTimeMillis - Date.now() > 0;
285291
}
286292

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)