diff --git a/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/UserNavigation/ImpersonateDialog/ImpersonateDialog.spec.tsx b/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/UserNavigation/ImpersonateDialog/ImpersonateDialog.spec.tsx index 0bca761a115..f005a1c6ead 100644 --- a/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/UserNavigation/ImpersonateDialog/ImpersonateDialog.spec.tsx +++ b/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/UserNavigation/ImpersonateDialog/ImpersonateDialog.spec.tsx @@ -1,14 +1,34 @@ import { MockedProvider } from '@apollo/client/testing' import { fireEvent, render, waitFor } from '@testing-library/react' +import { UserCredential, signInWithCustomToken } from 'firebase/auth' import { SnackbarProvider } from 'notistack' import { USER_IMPERSONATE } from './ImpersonateDialog' import { ImpersonateDialog } from '.' +const mockLoginWithCredential = jest.fn().mockResolvedValue(undefined) + +jest.mock('firebase/auth', () => ({ + getAuth: jest.fn(() => ({})), + signInWithCustomToken: jest.fn() +})) + +jest.mock('../../../../../libs/auth', () => ({ + loginWithCredential: (...args: unknown[]) => mockLoginWithCredential(...args) +})) + +const mockSignInWithCustomToken = signInWithCustomToken as jest.MockedFunction< + typeof signInWithCustomToken +> + const onClose = jest.fn() describe('JourneyView/Menu/ImpersonateDialog', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + it('should not set journey impersonate on close', async () => { const { getByRole } = render( @@ -27,6 +47,9 @@ describe('JourneyView/Menu/ImpersonateDialog', () => { }) it('should impersonate user on submit', async () => { + const credential = { user: {} } as unknown as UserCredential + mockSignInWithCustomToken.mockResolvedValue(credential) + const result = jest.fn(() => ({ data: { userImpersonate: 'accessToken' @@ -59,6 +82,12 @@ describe('JourneyView/Menu/ImpersonateDialog', () => { await waitFor(() => { expect(result).toHaveBeenCalled() }) + // signs in as the impersonated user, then refreshes the server-side + // session cookie so SSR runs as that user (otherwise journey edits 403) + await waitFor(() => { + expect(mockSignInWithCustomToken).toHaveBeenCalledWith({}, 'accessToken') + }) + expect(mockLoginWithCredential).toHaveBeenCalledWith(credential) }) it('shows notistack error alert when impersonate fails to update', async () => { diff --git a/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/UserNavigation/ImpersonateDialog/ImpersonateDialog.tsx b/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/UserNavigation/ImpersonateDialog/ImpersonateDialog.tsx index c8a0c31a960..7bfd78ce425 100644 --- a/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/UserNavigation/ImpersonateDialog/ImpersonateDialog.tsx +++ b/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/UserNavigation/ImpersonateDialog/ImpersonateDialog.tsx @@ -1,4 +1,4 @@ -import { ApolloError, gql, useApolloClient, useMutation } from '@apollo/client' +import { ApolloError, gql, useMutation } from '@apollo/client' import Alert from '@mui/material/Alert' import AlertTitle from '@mui/material/AlertTitle' import TextField from '@mui/material/TextField' @@ -15,6 +15,7 @@ import { UserImpersonate, UserImpersonateVariables } from '../../../../../../__generated__/UserImpersonate' +import { loginWithCredential } from '../../../../../libs/auth' export const USER_IMPERSONATE = gql` mutation UserImpersonate($email: String!) { @@ -35,7 +36,6 @@ export function ImpersonateDialog({ UserImpersonate, UserImpersonateVariables >(USER_IMPERSONATE) - const client = useApolloClient() const { enqueueSnackbar } = useSnackbar() const { t } = useTranslation('apps-journeys-admin') @@ -51,8 +51,14 @@ export function ImpersonateDialog({ }) if (data?.userImpersonate != null) { const auth = getAuth() - await signInWithCustomToken(auth, data.userImpersonate) - await client.resetStore() + const credential = await signInWithCustomToken( + auth, + data.userImpersonate + ) + // Refresh the server-side session cookie so SSR (e.g. the journey + // editor) runs as the impersonated user rather than the superuser. + // loginWithCredential reloads the page once the cookie is set. + await loginWithCredential(credential) } handleClose(formikHelpers.resetForm)() } catch (error) {