Skip to content

Commit

Permalink
Initialize App Check debug mode in initializeAppCheck (#5512)
Browse files Browse the repository at this point in the history
  • Loading branch information
hsubox76 authored Sep 23, 2021
1 parent 4e7aba4 commit 785a217
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 8 deletions.
46 changes: 46 additions & 0 deletions packages/app-check/src/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,13 @@ import * as logger from './logger';
import * as client from './client';
import * as storage from './storage';
import * as internalApi from './internal-api';
import * as indexeddb from './indexeddb';
import * as debug from './debug';
import { deleteApp, FirebaseApp } from '@firebase/app';
import { CustomProvider, ReCaptchaV3Provider } from './providers';
import { AppCheckService } from './factory';
import { AppCheckToken } from './public-types';
import { getDebugToken } from './debug';

describe('api', () => {
let app: FirebaseApp;
Expand Down Expand Up @@ -118,6 +121,49 @@ describe('api', () => {
})
).to.equal(appCheckInstance);
});
it('starts debug mode on first call', async () => {
let token: string = '';
const fakeWrite = (tokenToWrite: string): Promise<void> => {
token = tokenToWrite;
return Promise.resolve();
};
stub(indexeddb, 'writeDebugTokenToIndexedDB').callsFake(fakeWrite);
stub(indexeddb, 'readDebugTokenFromIndexedDB').resolves(token);
const consoleStub = stub(console, 'log');
self.FIREBASE_APPCHECK_DEBUG_TOKEN = true;
initializeAppCheck(app, {
provider: new ReCaptchaV3Provider(FAKE_SITE_KEY)
});
// Ensure getDebugToken() call inside `initializeAppCheck()`
// has time to resolve, and double check its value matches that
// written to indexedDB.
expect(await getDebugToken()).to.equal(token);
expect(consoleStub.args[0][0]).to.include(token);
self.FIREBASE_APPCHECK_DEBUG_TOKEN = undefined;
});
it('does not call initializeDebugMode on second call', async () => {
self.FIREBASE_APPCHECK_DEBUG_TOKEN = 'abcdefg';
const consoleStub = stub(console, 'log');
const initializeDebugModeSpy = spy(debug, 'initializeDebugMode');
// First call, should call initializeDebugMode()
initializeAppCheck(app, {
provider: new ReCaptchaV3Provider(FAKE_SITE_KEY)
});
expect(initializeDebugModeSpy).to.be.called;
initializeDebugModeSpy.resetHistory();
// Second call, should not call initializeDebugMode()
initializeAppCheck(app, {
provider: new ReCaptchaV3Provider(FAKE_SITE_KEY)
});
const token = await getDebugToken();
expect(token).to.equal('abcdefg');
// Two console logs of the token, for each initializeAppCheck call.
expect(consoleStub.args[0][0]).to.include(token);
expect(consoleStub.args[1][0]).to.include(token);
expect(consoleStub.args[1][0]).to.equal(consoleStub.args[0][0]);
expect(initializeDebugModeSpy).to.not.be.called;
self.FIREBASE_APPCHECK_DEBUG_TOKEN = undefined;
});

it('initialize reCAPTCHA when a ReCaptchaV3Provider is provided', () => {
const initReCAPTCHAStub = stub(reCAPTCHA, 'initialize').returns(
Expand Down
20 changes: 19 additions & 1 deletion packages/app-check/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
PartialObserver
} from './public-types';
import { ERROR_FACTORY, AppCheckError } from './errors';
import { getState, setState, AppCheckState } from './state';
import { getState, setState, AppCheckState, getDebugState } from './state';
import { FirebaseApp, getApp, _getProvider } from '@firebase/app';
import { getModularInstance, ErrorFn, NextFn } from '@firebase/util';
import { AppCheckService } from './factory';
Expand All @@ -35,6 +35,7 @@ import {
isValid
} from './internal-api';
import { readTokenFromStorage } from './storage';
import { getDebugToken, initializeDebugMode, isDebugMode } from './debug';

declare module '@firebase/component' {
interface NameServiceMapping {
Expand All @@ -57,6 +58,23 @@ export function initializeAppCheck(
app = getModularInstance(app);
const provider = _getProvider(app, 'app-check');

// Ensure initializeDebugMode() is only called once.
if (!getDebugState().initialized) {
initializeDebugMode();
}

// Log a message containing the debug token when `initializeAppCheck()`
// is called in debug mode.
if (isDebugMode()) {
// Do not block initialization to get the token for the message.
void getDebugToken().then(token =>
// Not using logger because I don't think we ever want this accidentally hidden.
console.log(
`App Check debug token: ${token}. You will need to add it to your app's App Check settings in the Firebase console for it to work.`
)
);
}

if (provider.isInitialized()) {
const existingInstance = provider.getImmediate();
const initialOptions = provider.getOptions() as unknown as AppCheckOptions;
Expand Down
6 changes: 5 additions & 1 deletion packages/app-check/src/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,18 @@ export async function getDebugToken(): Promise<string> {

export function initializeDebugMode(): void {
const globals = getGlobal();
const debugState = getDebugState();
// Set to true if this function has been called, whether or not
// it enabled debug mode.
debugState.initialized = true;

if (
typeof globals.FIREBASE_APPCHECK_DEBUG_TOKEN !== 'string' &&
globals.FIREBASE_APPCHECK_DEBUG_TOKEN !== true
) {
return;
}

const debugState = getDebugState();
debugState.enabled = true;
const deferredToken = new Deferred<string>();
debugState.token = deferredToken;
Expand Down
2 changes: 0 additions & 2 deletions packages/app-check/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import {
} from '@firebase/component';
import { _AppCheckComponentName } from './public-types';
import { factory, internalFactory } from './factory';
import { initializeDebugMode } from './debug';
import { _AppCheckInternalComponentName } from './types';
import { name, version } from '../package.json';

Expand Down Expand Up @@ -82,4 +81,3 @@ function registerAppCheck(): void {
}

registerAppCheck();
initializeDebugMode();
3 changes: 3 additions & 0 deletions packages/app-check/src/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export interface ReCAPTCHAState {
}

export interface DebugState {
initialized: boolean;
enabled: boolean;
token?: Deferred<string>;
}
Expand All @@ -52,6 +53,7 @@ export const DEFAULT_STATE: AppCheckState = {
};

const DEBUG_STATE: DebugState = {
initialized: false,
enabled: false
};

Expand All @@ -68,6 +70,7 @@ export function clearState(): void {
APP_CHECK_STATES.clear();
DEBUG_STATE.enabled = false;
DEBUG_STATE.token = undefined;
DEBUG_STATE.initialized = false;
}

export function getDebugState(): DebugState {
Expand Down
4 changes: 0 additions & 4 deletions packages/app-check/src/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,6 @@ export async function readOrCreateDebugTokenFromStorage(): Promise<string> {
writeDebugTokenToIndexedDB(newToken).catch(e =>
logger.warn(`Failed to persist debug token to IndexedDB. Error: ${e}`)
);
// Not using logger because I don't think we ever want this accidentally hidden?
console.log(
`App Check debug token: ${newToken}. You will need to add it to your app's App Check settings in the Firebase console for it to work`
);
return newToken;
} else {
return existingDebugToken;
Expand Down

0 comments on commit 785a217

Please sign in to comment.