Skip to content

Cache App Check debug token #5055

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jun 29, 2021
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/loud-lamps-camp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@firebase/app-check': patch
---

Fix an error causing App Check to log `HTTP status 429` errors in debug mode.
52 changes: 28 additions & 24 deletions packages/app-check/src/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
*/
import '../test/setup';
import { expect } from 'chai';
import { stub, spy } from 'sinon';
import { stub, spy, SinonStub } from 'sinon';
import {
activate,
setTokenAutoRefreshEnabled,
Expand All @@ -28,6 +28,7 @@ import {
getFakeApp,
getFakeCustomTokenProvider,
getFakePlatformLoggingProvider,
getFakeGreCAPTCHA,
removegreCAPTCHAScriptsOnPage
} from '../test/util';
import { clearState, getState } from './state';
Expand All @@ -37,8 +38,12 @@ import * as internalApi from './internal-api';
import * as client from './client';
import * as storage from './storage';
import * as logger from './logger';
import * as util from './util';

describe('api', () => {
beforeEach(() => {
stub(util, 'getRecaptcha').returns(getFakeGreCAPTCHA());
});
describe('activate()', () => {
let app: FirebaseApp;

Expand Down Expand Up @@ -126,25 +131,35 @@ describe('api', () => {
});
});
describe('onTokenChanged()', () => {
let storageReadStub: SinonStub;
let storageWriteStub: SinonStub;
const fakePlatformLoggingProvider = getFakePlatformLoggingProvider();
const fakeRecaptchaToken = 'fake-recaptcha-token';
const fakeRecaptchaAppCheckToken = {
token: 'fake-recaptcha-app-check-token',
expireTimeMillis: Date.now() + 60000,
issuedAtTimeMillis: 0
};

beforeEach(() => {
storageReadStub = stub(storage, 'readTokenFromStorage').resolves(
undefined
);
storageWriteStub = stub(storage, 'writeTokenToStorage');
});
afterEach(() => {
storageReadStub.restore();
storageWriteStub.restore();
clearState();
removegreCAPTCHAScriptsOnPage();
});
it('Listeners work when using top-level parameters pattern', async () => {
const app = getFakeApp({ automaticDataCollectionEnabled: true });
activate(app, FAKE_SITE_KEY, true);
const fakePlatformLoggingProvider = getFakePlatformLoggingProvider();
const fakeRecaptchaToken = 'fake-recaptcha-token';
const fakeRecaptchaAppCheckToken = {
token: 'fake-recaptcha-app-check-token',
expireTimeMillis: 123,
issuedAtTimeMillis: 0
};
const app = getFakeApp();
activate(app, FAKE_SITE_KEY, false);
stub(reCAPTCHA, 'getToken').returns(Promise.resolve(fakeRecaptchaToken));
stub(client, 'exchangeToken').returns(
Promise.resolve(fakeRecaptchaAppCheckToken)
);
stub(storage, 'writeTokenToStorage').returns(Promise.resolve(undefined));

const listener1 = (): void => {
throw new Error();
Expand Down Expand Up @@ -183,20 +198,12 @@ describe('api', () => {
});

it('Listeners work when using Observer pattern', async () => {
const app = getFakeApp({ automaticDataCollectionEnabled: true });
activate(app, FAKE_SITE_KEY, true);
const fakePlatformLoggingProvider = getFakePlatformLoggingProvider();
const fakeRecaptchaToken = 'fake-recaptcha-token';
const fakeRecaptchaAppCheckToken = {
token: 'fake-recaptcha-app-check-token',
expireTimeMillis: 123,
issuedAtTimeMillis: 0
};
const app = getFakeApp();
activate(app, FAKE_SITE_KEY, false);
stub(reCAPTCHA, 'getToken').returns(Promise.resolve(fakeRecaptchaToken));
stub(client, 'exchangeToken').returns(
Promise.resolve(fakeRecaptchaAppCheckToken)
);
stub(storage, 'writeTokenToStorage').returns(Promise.resolve(undefined));

const listener1 = (): void => {
throw new Error();
Expand Down Expand Up @@ -238,11 +245,8 @@ describe('api', () => {
stub(logger.logger, 'error');
const app = getFakeApp();
activate(app, FAKE_SITE_KEY, false);
const fakePlatformLoggingProvider = getFakePlatformLoggingProvider();
const fakeRecaptchaToken = 'fake-recaptcha-token';
stub(reCAPTCHA, 'getToken').returns(Promise.resolve(fakeRecaptchaToken));
stub(client, 'exchangeToken').rejects('exchange error');
stub(storage, 'writeTokenToStorage').returns(Promise.resolve(undefined));

const listener1 = spy();

Expand Down
4 changes: 3 additions & 1 deletion packages/app-check/src/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ describe('client', () => {
});

it('returns a AppCheck token', async () => {
useFakeTimers();
// To get a consistent expireTime/issuedAtTime.
const clock = useFakeTimers();
fetchStub.returns(
Promise.resolve({
status: 200,
Expand All @@ -77,6 +78,7 @@ describe('client', () => {
expireTimeMillis: 3600,
issuedAtTimeMillis: 0
});
clock.restore();
});

it('throws when there is a network error', async () => {
Expand Down
18 changes: 15 additions & 3 deletions packages/app-check/src/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,21 @@ import {
import { Provider } from '@firebase/component';
import { PartialObserver } from '@firebase/util';

import { FirebaseService } from '@firebase/app-types/private';
import { getState } from './state';

export function factory(
app: FirebaseApp,
platformLoggerProvider: Provider<'platform-logger'>
): FirebaseAppCheck {
): FirebaseAppCheck & FirebaseService {
return {
app,
activate: (
siteKeyOrProvider: string | AppCheckProvider,
isTokenAutoRefreshEnabled?: boolean
) => activate(app, siteKeyOrProvider, isTokenAutoRefreshEnabled),
setTokenAutoRefreshEnabled: (isTokenAutoRefreshEnabled: boolean) =>
setTokenAutoRefreshEnabled(app, isTokenAutoRefreshEnabled),

getToken: forceRefresh =>
getToken(app, platformLoggerProvider, forceRefresh),
onTokenChanged: (
Expand All @@ -68,7 +71,16 @@ export function factory(
onNextOrObserver as (tokenResult: AppCheckTokenResult) => void,
onError,
onCompletion
)
),
INTERNAL: {
delete: () => {
const { tokenObservers } = getState(app);
for (const tokenObserver of tokenObservers) {
removeTokenListener(app, tokenObserver.next);
}
return Promise.resolve();
}
}
};
}

Expand Down
Loading