Skip to content

Commit

Permalink
[Auth] Fix errors when cookies are disabled in Chrome (compat layer) (#…
Browse files Browse the repository at this point in the history
…5923)

* Fix bug in compat layer when cookies are disabled

* Formatting

* Changeset

* Add delay to (negative) tests
  • Loading branch information
sam-gc committed Jan 24, 2022
1 parent 2d04af9 commit 4983f4d
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 11 deletions.
6 changes: 6 additions & 0 deletions .changeset/gold-geckos-carry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@firebase/auth-compat": patch
"@firebase/auth": patch
---

Fix errors in compatibility layer when cookies are fully disabled in Chrome
71 changes: 70 additions & 1 deletion packages/auth-compat/src/auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,14 @@ import * as sinon from 'sinon';
import sinonChai from 'sinon-chai';
import { Auth } from './auth';
import { CompatPopupRedirectResolver } from './popup_redirect';
import * as platform from './platform';

use(sinonChai);

function delay(ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms));
}

// For the most part, the auth methods just call straight through. Some parts
// of the auth compat layer are more complicated: these tests cover those
describe('auth compat', () => {
Expand All @@ -45,7 +50,7 @@ describe('auth compat', () => {
});

afterEach(() => {
sinon.restore;
sinon.restore();
});

it('saves the persistence into session storage if available', async () => {
Expand Down Expand Up @@ -75,6 +80,40 @@ describe('auth compat', () => {
}
});

it('does not save persistence if property throws DOMException', async () => {
if (typeof self !== 'undefined') {
sinon.stub(platform, '_getSelfWindow').returns({
get sessionStorage(): Storage {
throw new DOMException('Nope!');
}
} as unknown as Window);
const setItemSpy = sinon.spy(sessionStorage, 'setItem');
sinon.stub(underlyingAuth, '_getPersistence').returns('TEST');
sinon
.stub(underlyingAuth, '_initializationPromise')
.value(Promise.resolve());
sinon.stub(
exp._getInstance<exp.PopupRedirectResolverInternal>(
CompatPopupRedirectResolver
),
'_openRedirect'
);
providerStub.isInitialized.returns(true);
providerStub.getImmediate.returns(underlyingAuth);
const authCompat = new Auth(
app,
providerStub as unknown as Provider<'auth'>
);
// eslint-disable-next-line @typescript-eslint/no-floating-promises
await authCompat.signInWithRedirect(new exp.GoogleAuthProvider());
await delay(50);
expect(setItemSpy).not.to.have.been.calledWith(
'firebase:persistence:api-key:undefined',
'TEST'
);
}
});

it('pulls the persistence and sets as the main persitsence if set', () => {
if (typeof self !== 'undefined') {
sessionStorage.setItem(
Expand All @@ -98,5 +137,35 @@ describe('auth compat', () => {
});
}
});

it('does not die if sessionStorage errors', async () => {
if (typeof self !== 'undefined') {
sinon.stub(platform, '_getSelfWindow').returns({
get sessionStorage(): Storage {
throw new DOMException('Nope!');
}
} as unknown as Window);
sessionStorage.setItem(
'firebase:persistence:api-key:undefined',
'none'
);
providerStub.isInitialized.returns(false);
providerStub.initialize.returns(underlyingAuth);
new Auth(app, providerStub as unknown as Provider<'auth'>);
// eslint-disable-next-line @typescript-eslint/no-floating-promises
await delay(50);
expect(providerStub.initialize).to.have.been.calledWith({
options: {
popupRedirectResolver: CompatPopupRedirectResolver,
persistence: [
exp.indexedDBLocalPersistence,
exp.browserLocalPersistence,
exp.browserSessionPersistence,
exp.inMemoryPersistence
]
}
});
}
});
});
});
24 changes: 14 additions & 10 deletions packages/auth-compat/src/persistence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

import * as exp from '@firebase/auth/internal';
import { isIndexedDBAvailable, isNode, isReactNative } from '@firebase/util';
import { _isWebStorageSupported, _isWorker } from './platform';
import { _getSelfWindow, _isWebStorageSupported, _isWorker } from './platform';

export const Persistence = {
LOCAL: 'local',
Expand Down Expand Up @@ -84,29 +84,28 @@ export async function _savePersistenceForRedirect(
auth: exp.AuthInternal
): Promise<void> {
await auth._initializationPromise;

const win = getSelfWindow();
const session = getSessionStorageIfAvailable();
const key = exp._persistenceKeyName(
PERSISTENCE_KEY,
auth.config.apiKey,
auth.name
);
if (win?.sessionStorage) {
win.sessionStorage.setItem(key, auth._getPersistence());
if (session) {
session.setItem(key, auth._getPersistence());
}
}

export function _getPersistencesFromRedirect(
apiKey: string,
appName: string
): exp.Persistence[] {
const win = getSelfWindow();
if (!win?.sessionStorage) {
const session = getSessionStorageIfAvailable();
if (!session) {
return [];
}

const key = exp._persistenceKeyName(PERSISTENCE_KEY, apiKey, appName);
const persistence = win.sessionStorage.getItem(key);
const persistence = session.getItem(key);

switch (persistence) {
case Persistence.NONE:
Expand All @@ -120,6 +119,11 @@ export function _getPersistencesFromRedirect(
}
}

function getSelfWindow(): Window | null {
return typeof window !== 'undefined' ? window : null;
/** Returns session storage, or null if the property access errors */
function getSessionStorageIfAvailable(): Storage | null {
try {
return _getSelfWindow()?.sessionStorage || null;
} catch (e) {
return null;
}
}
4 changes: 4 additions & 0 deletions packages/auth-compat/src/platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,7 @@ export async function _isCordova(): Promise<boolean> {
});
});
}

export function _getSelfWindow(): Window | null {
return typeof window !== 'undefined' ? window : null;
}

0 comments on commit 4983f4d

Please sign in to comment.