Skip to content

Commit

Permalink
[IA-4620] invalidate cookies on sign out (#4494)
Browse files Browse the repository at this point in the history
Co-authored-by: jdcanas <jcanas@broadinstitute.com>
  • Loading branch information
jdcanas and jdcanas authored Jan 5, 2024
1 parent f0c5c77 commit 5189b74
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 28 deletions.
14 changes: 6 additions & 8 deletions src/auth/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
OidcUser,
revokeTokens,
} from 'src/auth/oidc-broker';
import { cookieProvider } from 'src/components/CookieProvider';
import { cookiesAcceptedKey } from 'src/components/CookieWarning';
import { Ajax } from 'src/libs/ajax';
import { fetchOk } from 'src/libs/ajax/ajax-common';
Expand All @@ -27,9 +28,7 @@ import {
asyncImportJobStore,
AuthState,
authStore,
azureCookieReadyStore,
azurePreviewStore,
cookieReadyStore,
getTerraUser,
MetricState,
metricStore,
Expand Down Expand Up @@ -101,18 +100,17 @@ export const sendAuthTokenDesyncMetric = () => {
Ajax().Metrics.captureEvent(Events.user.authToken.desync, {});
};

export const signOut = (cause: SignOutCause = 'unspecified'): void => {
export const signOut = async (cause: SignOutCause = 'unspecified') => {
sendSignOutMetrics(cause);
if (cause === 'expiredRefreshToken' || cause === 'errorRefreshingAuthToken') {
notify('info', sessionTimedOutErrorMessage, sessionTimeoutProps);
}
// TODO: invalidate runtime cookies https://broadworkbench.atlassian.net/browse/IA-3498
cookieReadyStore.reset();
azureCookieReadyStore.reset();
getSessionStorage().clear();

azurePreviewStore.set(false);

revokeTokens();
await cookieProvider.invalidateCookies();
getSessionStorage().clear();
await revokeTokens();

const { cookiesAccepted } = authStore.get();

Expand Down
44 changes: 44 additions & 0 deletions src/components/CookieProvider.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { cookieProvider } from 'src/components/CookieProvider';
import { Ajax } from 'src/libs/ajax';
import { RuntimesAjaxContract } from 'src/libs/ajax/leonardo/Runtimes';
import { asMockedFn } from 'src/testing/test-utils';

jest.mock('src/libs/ajax');

/**
* local test utility - mocks the Ajax super-object and the subset of needed multi-contracts it
* returns with as much type-safety as possible.
*
* @return collection of key contract sub-objects for easy
* mock overrides and/or method spying/assertions
*/
type AjaxContract = ReturnType<typeof Ajax>;
type RuntimesNeeds = Pick<RuntimesAjaxContract, 'invalidateCookie'>;
interface AjaxMockNeeds {
Runtimes: RuntimesNeeds;
}

const mockAjaxNeeds = (): AjaxMockNeeds => {
const partialRuntimes: RuntimesNeeds = {
invalidateCookie: jest.fn(),
};
const mockRuntimes = partialRuntimes as RuntimesAjaxContract;

asMockedFn(Ajax).mockReturnValue({ Runtimes: mockRuntimes } as AjaxContract);

return {
Runtimes: partialRuntimes,
};
};
describe('CookieProvider', () => {
it('calls the leo endpoint on invalidateCookie', async () => {
const ajaxMock = mockAjaxNeeds();

// Act
await cookieProvider.invalidateCookies();

// Assert
expect(Ajax).toBeCalledTimes(1);
expect(ajaxMock.Runtimes.invalidateCookie).toBeCalledTimes(1);
});
});
18 changes: 18 additions & 0 deletions src/components/CookieProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Ajax } from 'src/libs/ajax';
import { AbortOption } from 'src/libs/ajax/data-provider-common';
import { azureCookieReadyStore, cookieReadyStore } from 'src/libs/state';

export interface CookieProvider {
invalidateCookies: (options?: AbortOption) => Promise<void>;
}

export const cookieProvider: CookieProvider = {
invalidateCookies: async (options: AbortOption = {}) => {
const { signal } = options;
// TODO: call azure invalidate cookie once endpoint exists, https://broadworkbench.atlassian.net/browse/IA-3498
await Ajax(signal).Runtimes.invalidateCookie();

cookieReadyStore.reset();
azureCookieReadyStore.reset();
},
};
24 changes: 5 additions & 19 deletions src/components/CookieWarning.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { useEffect, useRef, useState } from 'react';
import { aside, div, h } from 'react-hyperscript-helpers';
import { Transition } from 'react-transition-group';
import { ButtonPrimary, ButtonSecondary, Link } from 'src/components/common';
import { Ajax } from 'src/libs/ajax';
import { cookieProvider } from 'src/components/CookieProvider';
import { getEnabledBrand } from 'src/libs/brand-utils';
import { getSessionStorage } from 'src/libs/browser-storage';
import colors from 'src/libs/colors';
import * as Nav from 'src/libs/nav';
import { useCancellation, useStore } from 'src/libs/react-utils';
import { authStore, azureCookieReadyStore, cookieReadyStore } from 'src/libs/state';
import { authStore } from 'src/libs/state';

export const cookiesAcceptedKey = 'cookiesAccepted';

Expand All @@ -22,10 +22,10 @@ const transitionStyle = {

const CookieWarning = () => {
const animTime = 0.3;
const signal = useCancellation();
const [showWarning, setShowWarning] = useState(false);
const { cookiesAccepted } = useStore(authStore);
const timeout = useRef();
const signal = useCancellation();
const brand = getEnabledBrand();

const acceptCookies = (acceptedCookies) => {
Expand All @@ -45,23 +45,9 @@ const CookieWarning = () => {
}, [cookiesAccepted]);

const rejectCookies = async () => {
const cookies = document.cookie.split(';');
acceptCookies(false);
// TODO: call azure invalidate cookie once endpoint exists, https://broadworkbench.atlassian.net/browse/IA-3498
await Ajax(signal)
.Runtimes.invalidateCookie()
.catch(() => {});
// Expire all cookies
_.forEach((cookie) => {
// Find an equals sign and uses it to grab the substring of the cookie that is its name
const eqPos = cookie.indexOf('=');
const cookieName = eqPos > -1 ? cookie.substr(0, eqPos) : cookie;
document.cookie = `${cookieName}=;expires=Thu, 01 Jan 1970 00:00:00 GMT`;
}, cookies);

cookieReadyStore.reset();
azureCookieReadyStore.reset();
await cookieProvider.invalidateCookies({ signal });
getSessionStorage().clear();
acceptCookies(false);
};

return h(
Expand Down
2 changes: 1 addition & 1 deletion src/libs/ajax/leonardo/Runtimes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ export const Runtimes = (signal: AbortSignal) => {
},

invalidateCookie: () => {
return fetchLeo('proxy/invalidateToken', _.merge(authOpts(), { signal }));
return fetchLeo('proxy/invalidateToken', _.merge(authOpts(), { signal, credentials: 'include' }));
},

setCookie: () => {
Expand Down

0 comments on commit 5189b74

Please sign in to comment.