Skip to content

Add the forceRefresh parameter to Installations' getToken method #2239

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 2 commits into from
Oct 8, 2019
Merged
Show file tree
Hide file tree
Changes from all 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
36 changes: 27 additions & 9 deletions packages/installations/src/functions/get-token.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ import { sleep } from '../util/sleep';
import { getToken } from './get-token';

const FID = 'dont-talk-to-strangers';
const AUTH_TOKEN = 'authTokenFromServer';
const AUTH_TOKEN = 'authToken';
const NEW_AUTH_TOKEN = 'newAuthToken';
const ONE_WEEK_MS = 7 * 24 * 60 * 60 * 1000;

/**
Expand Down Expand Up @@ -104,7 +105,7 @@ const setupInstallationEntryMap: Map<
const updatedEntry: RegisteredInstallationEntry = {
...entry,
authToken: {
token: AUTH_TOKEN,
token: NEW_AUTH_TOKEN,
expiresIn: ONE_WEEK_MS,
requestStatus: RequestStatus.COMPLETED,
creationTime: Date.now()
Expand Down Expand Up @@ -196,7 +197,7 @@ describe('getToken', () => {
registrationStatus: RequestStatus.COMPLETED,
refreshToken: 'refreshToken',
authToken: {
token: AUTH_TOKEN,
token: NEW_AUTH_TOKEN,
expiresIn: ONE_WEEK_MS,
requestStatus: RequestStatus.COMPLETED,
creationTime: Date.now()
Expand All @@ -210,7 +211,7 @@ describe('getToken', () => {
).callsFake(async () => {
await sleep(100); // Request would take some time
const result: CompletedAuthToken = {
token: AUTH_TOKEN,
token: NEW_AUTH_TOKEN,
expiresIn: ONE_WEEK_MS,
requestStatus: RequestStatus.COMPLETED,
creationTime: Date.now()
Expand All @@ -226,7 +227,7 @@ describe('getToken', () => {

it('resolves with an auth token', async () => {
const token = await getToken(app);
expect(token).to.equal(AUTH_TOKEN);
expect(token).to.be.oneOf([AUTH_TOKEN, NEW_AUTH_TOKEN]);
});

it('saves the token in the DB', async () => {
Expand Down Expand Up @@ -382,16 +383,27 @@ describe('getToken', () => {
expect(generateAuthTokenSpy).not.to.be.called;
});

it('refreshes the token if forceRefresh is true', async () => {
const token = await getToken(app, true);
expect(token).to.equal(NEW_AUTH_TOKEN);
expect(generateAuthTokenSpy).to.be.called;
});

it('works even if the app is offline', async () => {
stub(navigator, 'onLine').value(false);

const token = await getToken(app);
expect(token).to.equal(AUTH_TOKEN);
});

it('throws if the app is offline and forceRefresh is true', async () => {
stub(navigator, 'onLine').value(false);

await expect(getToken(app, true)).to.be.rejected;
});
});

describe('when there is an auth token that is about to expire in the DB', () => {
const DB_AUTH_TOKEN = 'authTokenFromDB';
let clock: SinonFakeTimers;

beforeEach(async () => {
Expand All @@ -401,7 +413,7 @@ describe('getToken', () => {
registrationStatus: RequestStatus.COMPLETED,
refreshToken: 'refreshToken',
authToken: {
token: DB_AUTH_TOKEN,
token: AUTH_TOKEN,
expiresIn: ONE_WEEK_MS,
requestStatus: RequestStatus.COMPLETED,
creationTime:
Expand All @@ -414,13 +426,13 @@ describe('getToken', () => {

it('returns a different token after expiration', async () => {
const token1 = await getToken(app);
expect(token1).to.equal(DB_AUTH_TOKEN);
expect(token1).to.equal(AUTH_TOKEN);

// Wait 30 minutes.
clock.tick('30:00');

const token2 = await getToken(app);
await expect(token2).to.equal(AUTH_TOKEN);
await expect(token2).to.equal(NEW_AUTH_TOKEN);
await expect(token2).not.to.equal(token1);
});
});
Expand All @@ -441,6 +453,12 @@ describe('getToken', () => {
await set(appConfig, installationEntry);
});

it('returns a different token', async () => {
const token = await getToken(app);
expect(token).to.equal(NEW_AUTH_TOKEN);
expect(generateAuthTokenSpy).to.be.called;
});

it('throws if the app is offline', async () => {
stub(navigator, 'onLine').value(false);

Expand Down
7 changes: 5 additions & 2 deletions packages/installations/src/functions/get-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,17 @@ import { AppConfig } from '../interfaces/app-config';
import { RequestStatus } from '../interfaces/installation-entry';
import { ERROR_FACTORY, ErrorCode } from '../util/errors';

export async function getToken(app: FirebaseApp): Promise<string> {
export async function getToken(
app: FirebaseApp,
forceRefresh = false
): Promise<string> {
const appConfig = extractAppConfig(app);

await completeInstallationRegistration(appConfig);

// At this point we either have a Registered Installation in the DB, or we've
// already thrown an error.
return refreshAuthToken(appConfig);
return refreshAuthToken(appConfig, forceRefresh);
}

async function completeInstallationRegistration(
Expand Down
7 changes: 5 additions & 2 deletions packages/installations/src/helpers/refresh-auth-token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ import { remove, set, update } from './idb-manager';
*
* Should only be called if the Firebase Installation is registered.
*/
export async function refreshAuthToken(appConfig: AppConfig): Promise<string> {
export async function refreshAuthToken(
appConfig: AppConfig,
forceRefresh = false
): Promise<string> {
let tokenPromise: Promise<CompletedAuthToken> | undefined;
const entry = await update(
appConfig,
Expand All @@ -46,7 +49,7 @@ export async function refreshAuthToken(appConfig: AppConfig): Promise<string> {
}

const oldAuthToken = oldEntry.authToken;
if (isAuthTokenValid(oldAuthToken)) {
if (!forceRefresh && isAuthTokenValid(oldAuthToken)) {
// There is a valid token in the DB.
return oldEntry;
} else if (oldAuthToken.requestStatus === RequestStatus.IN_PROGRESS) {
Expand Down
2 changes: 1 addition & 1 deletion packages/installations/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function registerInstallations(instance: _FirebaseNamespace): void {
return {
app,
getId: () => getId(app),
getToken: () => getToken(app),
getToken: (forceRefresh?: boolean) => getToken(app, forceRefresh),
delete: () => deleteInstallation(app)
};
};
Expand Down