Skip to content

Commit a7f4a2e

Browse files
authored
App Check heartbeat implementation (#5967)
1 parent ddeff83 commit a7f4a2e

File tree

8 files changed

+44
-32
lines changed

8 files changed

+44
-32
lines changed

.changeset/polite-donkeys-divide.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@firebase/app-check': patch
3+
---
4+
5+
Update platform logging to use new endpoint.

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

+6-6
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import '../test/setup';
1919
import { expect } from 'chai';
2020
import { stub, SinonStub, useFakeTimers } from 'sinon';
2121
import { FirebaseApp } from '@firebase/app';
22-
import { getFakeApp, getFakePlatformLoggingProvider } from '../test/util';
22+
import { getFakeApp, getFakeHeartbeatServiceProvider } from '../test/util';
2323
import {
2424
getExchangeRecaptchaV3TokenRequest,
2525
exchangeToken,
@@ -86,7 +86,7 @@ describe('client', () => {
8686

8787
const response = await exchangeToken(
8888
getExchangeRecaptchaV3TokenRequest(app, 'fake-custom-token'),
89-
getFakePlatformLoggingProvider('a/1.2.3 fire-app-check/2.3.4')
89+
getFakeHeartbeatServiceProvider('a/1.2.3 fire-app-check/2.3.4')
9090
);
9191

9292
expect(
@@ -114,7 +114,7 @@ describe('client', () => {
114114
try {
115115
await exchangeToken(
116116
getExchangeRecaptchaV3TokenRequest(app, 'fake-custom-token'),
117-
getFakePlatformLoggingProvider()
117+
getFakeHeartbeatServiceProvider()
118118
);
119119
} catch (e) {
120120
expect(e).instanceOf(FirebaseError);
@@ -143,7 +143,7 @@ describe('client', () => {
143143
try {
144144
await exchangeToken(
145145
getExchangeRecaptchaV3TokenRequest(app, 'fake-custom-token'),
146-
getFakePlatformLoggingProvider()
146+
getFakeHeartbeatServiceProvider()
147147
);
148148
} catch (e) {
149149
expect(e).instanceOf(FirebaseError);
@@ -171,7 +171,7 @@ describe('client', () => {
171171
try {
172172
await exchangeToken(
173173
getExchangeRecaptchaV3TokenRequest(app, 'fake-custom-token'),
174-
getFakePlatformLoggingProvider()
174+
getFakeHeartbeatServiceProvider()
175175
);
176176
} catch (e) {
177177
expect(e).instanceOf(FirebaseError);
@@ -205,7 +205,7 @@ describe('client', () => {
205205
try {
206206
await exchangeToken(
207207
getExchangeRecaptchaV3TokenRequest(app, 'fake-custom-token'),
208-
getFakePlatformLoggingProvider()
208+
getFakeHeartbeatServiceProvider()
209209
);
210210
} catch (e) {
211211
expect(e).instanceOf(FirebaseError);

packages/app-check/src/client.ts

+8-5
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,20 @@ interface AppCheckRequest {
4242

4343
export async function exchangeToken(
4444
{ url, body }: AppCheckRequest,
45-
platformLoggerProvider: Provider<'platform-logger'>
45+
heartbeatServiceProvider: Provider<'heartbeat'>
4646
): Promise<AppCheckTokenInternal> {
4747
const headers: HeadersInit = {
4848
'Content-Type': 'application/json'
4949
};
50-
// If platform logger exists, add the platform info string to the header.
51-
const platformLogger = platformLoggerProvider.getImmediate({
50+
// If heartbeat service exists, add heartbeat header string to the header.
51+
const heartbeatService = heartbeatServiceProvider.getImmediate({
5252
optional: true
5353
});
54-
if (platformLogger) {
55-
headers['X-Firebase-Client'] = platformLogger.getPlatformInfoString();
54+
if (heartbeatService) {
55+
const heartbeatsHeader = await heartbeatService.getHeartbeatsHeader();
56+
if (heartbeatsHeader) {
57+
headers['X-Firebase-Client'] = heartbeatsHeader;
58+
}
5659
}
5760
const options: RequestInit = {
5861
method: 'POST',

packages/app-check/src/factory.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import { getState } from './state';
3232
export class AppCheckService implements AppCheck, _FirebaseService {
3333
constructor(
3434
public app: FirebaseApp,
35-
public platformLoggerProvider: Provider<'platform-logger'>
35+
public heartbeatServiceProvider: Provider<'heartbeat'>
3636
) {}
3737
_delete(): Promise<void> {
3838
const { tokenObservers } = getState(this.app);
@@ -45,9 +45,9 @@ export class AppCheckService implements AppCheck, _FirebaseService {
4545

4646
export function factory(
4747
app: FirebaseApp,
48-
platformLoggerProvider: Provider<'platform-logger'>
48+
heartbeatServiceProvider: Provider<'heartbeat'>
4949
): AppCheckService {
50-
return new AppCheckService(app, platformLoggerProvider);
50+
return new AppCheckService(app, heartbeatServiceProvider);
5151
}
5252

5353
export function internalFactory(

packages/app-check/src/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ function registerAppCheck(): void {
4848
container => {
4949
// getImmediate for FirebaseApp will always succeed
5050
const app = container.getProvider('app').getImmediate();
51-
const platformLoggerProvider = container.getProvider('platform-logger');
52-
return factory(app, platformLoggerProvider);
51+
const heartbeatServiceProvider = container.getProvider('heartbeat');
52+
return factory(app, heartbeatServiceProvider);
5353
},
5454
ComponentType.PUBLIC
5555
)

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export async function getToken(
106106
if (!state.exchangeTokenPromise) {
107107
state.exchangeTokenPromise = exchangeToken(
108108
getExchangeDebugTokenRequest(app, await getDebugToken()),
109-
appCheck.platformLoggerProvider
109+
appCheck.heartbeatServiceProvider
110110
).then(token => {
111111
state.exchangeTokenPromise = undefined;
112112
return token;

packages/app-check/src/providers.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ import { getDurationString } from './util';
4646
*/
4747
export class ReCaptchaV3Provider implements AppCheckProvider {
4848
private _app?: FirebaseApp;
49-
private _platformLoggerProvider?: Provider<'platform-logger'>;
49+
private _heartbeatServiceProvider?: Provider<'heartbeat'>;
5050
/**
5151
* Throttle requests on certain error codes to prevent too many retries
5252
* in a short time.
@@ -66,7 +66,7 @@ export class ReCaptchaV3Provider implements AppCheckProvider {
6666
throwIfThrottled(this._throttleData);
6767

6868
// Top-level `getToken()` has already checked that App Check is initialized
69-
// and therefore this._app and this._platformLoggerProvider are available.
69+
// and therefore this._app and this._heartbeatServiceProvider are available.
7070
const attestedClaimsToken = await getReCAPTCHAToken(this._app!).catch(
7171
_e => {
7272
// reCaptcha.execute() throws null which is not very descriptive.
@@ -77,7 +77,7 @@ export class ReCaptchaV3Provider implements AppCheckProvider {
7777
try {
7878
result = await exchangeToken(
7979
getExchangeRecaptchaV3TokenRequest(this._app!, attestedClaimsToken),
80-
this._platformLoggerProvider!
80+
this._heartbeatServiceProvider!
8181
);
8282
} catch (e) {
8383
if ((e as FirebaseError).code === AppCheckError.FETCH_STATUS_ERROR) {
@@ -105,7 +105,7 @@ export class ReCaptchaV3Provider implements AppCheckProvider {
105105
*/
106106
initialize(app: FirebaseApp): void {
107107
this._app = app;
108-
this._platformLoggerProvider = _getProvider(app, 'platform-logger');
108+
this._heartbeatServiceProvider = _getProvider(app, 'heartbeat');
109109
initializeRecaptchaV3(app, this._siteKey).catch(() => {
110110
/* we don't care about the initialization result */
111111
});
@@ -131,7 +131,7 @@ export class ReCaptchaV3Provider implements AppCheckProvider {
131131
*/
132132
export class ReCaptchaEnterpriseProvider implements AppCheckProvider {
133133
private _app?: FirebaseApp;
134-
private _platformLoggerProvider?: Provider<'platform-logger'>;
134+
private _heartbeatServiceProvider?: Provider<'heartbeat'>;
135135
/**
136136
* Throttle requests on certain error codes to prevent too many retries
137137
* in a short time.
@@ -150,7 +150,7 @@ export class ReCaptchaEnterpriseProvider implements AppCheckProvider {
150150
async getToken(): Promise<AppCheckTokenInternal> {
151151
throwIfThrottled(this._throttleData);
152152
// Top-level `getToken()` has already checked that App Check is initialized
153-
// and therefore this._app and this._platformLoggerProvider are available.
153+
// and therefore this._app and this._heartbeatServiceProvider are available.
154154
const attestedClaimsToken = await getReCAPTCHAToken(this._app!).catch(
155155
_e => {
156156
// reCaptcha.execute() throws null which is not very descriptive.
@@ -164,7 +164,7 @@ export class ReCaptchaEnterpriseProvider implements AppCheckProvider {
164164
this._app!,
165165
attestedClaimsToken
166166
),
167-
this._platformLoggerProvider!
167+
this._heartbeatServiceProvider!
168168
);
169169
} catch (e) {
170170
if ((e as FirebaseError).code === AppCheckError.FETCH_STATUS_ERROR) {
@@ -192,7 +192,7 @@ export class ReCaptchaEnterpriseProvider implements AppCheckProvider {
192192
*/
193193
initialize(app: FirebaseApp): void {
194194
this._app = app;
195-
this._platformLoggerProvider = _getProvider(app, 'platform-logger');
195+
this._heartbeatServiceProvider = _getProvider(app, 'heartbeat');
196196
initializeRecaptchaEnterprise(app, this._siteKey).catch(() => {
197197
/* we don't care about the initialization result */
198198
});

packages/app-check/test/util.ts

+11-7
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
} from '@firebase/component';
3131
import { AppCheckService } from '../src/factory';
3232
import { AppCheck, CustomProvider } from '../src';
33+
import { HeartbeatService } from '@firebase/app/dist/app/src/types';
3334

3435
export const FAKE_SITE_KEY = 'fake-site-key';
3536

@@ -55,15 +56,15 @@ export function getFakeApp(overrides: Record<string, any> = {}): FirebaseApp {
5556
export function getFakeAppCheck(app: FirebaseApp): AppCheck {
5657
return {
5758
app,
58-
platformLoggerProvider: getFakePlatformLoggingProvider()
59+
heartbeatServiceProvider: getFakeHeartbeatServiceProvider()
5960
} as AppCheck;
6061
}
6162

6263
export function getFullApp(): FirebaseApp {
6364
const app = initializeApp(fakeConfig);
6465
_registerComponent(
6566
new Component(
66-
'platform-logger',
67+
'heartbeat',
6768
() => {
6869
return {} as any;
6970
},
@@ -92,19 +93,22 @@ export function getFakeCustomTokenProvider(): CustomProvider {
9293
});
9394
}
9495

95-
export function getFakePlatformLoggingProvider(
96+
export function getFakeHeartbeatServiceProvider(
9697
fakeLogString: string = 'a/1.2.3 b/2.3.4'
97-
): Provider<'platform-logger'> {
98+
): Provider<'heartbeat'> {
9899
const container = new ComponentContainer('test');
99100
container.addComponent(
100101
new Component(
101-
'platform-logger',
102-
() => ({ getPlatformInfoString: () => fakeLogString }),
102+
'heartbeat',
103+
() =>
104+
({
105+
getHeartbeatsHeader: () => Promise.resolve(fakeLogString)
106+
} as HeartbeatService),
103107
ComponentType.PRIVATE
104108
)
105109
);
106110

107-
return container.getProvider('platform-logger');
111+
return container.getProvider('heartbeat');
108112
}
109113

110114
export function getFakeGreCAPTCHA(

0 commit comments

Comments
 (0)