diff --git a/src/frontend/with-page-auth-required.tsx b/src/frontend/with-page-auth-required.tsx index c3d768844..9827b5457 100644 --- a/src/frontend/with-page-auth-required.tsx +++ b/src/frontend/with-page-auth-required.tsx @@ -1,5 +1,4 @@ import React, { ComponentType, useEffect } from 'react'; -import { useRouter } from 'next/router'; import { useConfig } from './use-config'; import { useUser } from './use-user'; @@ -73,18 +72,22 @@ export type WithPageAuthRequired =

( */ const withPageAuthRequired: WithPageAuthRequired = (Component, options = {}) => { return function withPageAuthRequired(props): JSX.Element { - const router = useRouter(); - const { - returnTo = `${router.basePath ?? ''}${router.asPath}`, - onRedirecting = defaultOnRedirecting, - onError = defaultOnError - } = options; + const { returnTo, onRedirecting = defaultOnRedirecting, onError = defaultOnError } = options; const { loginUrl } = useConfig(); const { user, error, isLoading } = useUser(); useEffect(() => { if ((user && !error) || isLoading) return; - window.location.assign(`${loginUrl}?returnTo=${encodeURIComponent(returnTo)}`); + let returnToPath: string; + + if (!returnTo) { + const currentLocation = window.location.toString(); + returnToPath = currentLocation.replace(new URL(currentLocation).origin, '') || '/'; + } else { + returnToPath = returnTo; + } + + window.location.assign(`${loginUrl}?returnTo=${encodeURIComponent(returnToPath)}`); }, [user, error, isLoading]); if (error) return onError(error); diff --git a/tests/frontend/use-config.test.ts b/tests/frontend/use-config.test.ts index e4969d508..a17210ec9 100644 --- a/tests/frontend/use-config.test.ts +++ b/tests/frontend/use-config.test.ts @@ -3,10 +3,6 @@ import { renderHook } from '@testing-library/react-hooks'; import { withConfigProvider } from '../fixtures/frontend'; import { useConfig } from '../../src/frontend/use-config'; -jest.mock('next/router', () => ({ - useRouter: (): any => ({ asPath: '/' }) -})); - describe('context wrapper', () => { test('should provide the default login url', async () => { const { result } = renderHook(() => useConfig(), { diff --git a/tests/frontend/use-user.test.tsx b/tests/frontend/use-user.test.tsx index 44d68433c..516d5e598 100644 --- a/tests/frontend/use-user.test.tsx +++ b/tests/frontend/use-user.test.tsx @@ -11,10 +11,6 @@ import { useConfig } from '../../src/frontend'; import { useUser, UserContext } from '../../src'; import React from 'react'; -jest.mock('next/router', () => ({ - useRouter: (): any => ({ asPath: '/' }) -})); - describe('context wrapper', () => { afterEach(() => delete (global as any).fetch); diff --git a/tests/frontend/with-page-auth-required.test.tsx b/tests/frontend/with-page-auth-required.test.tsx index 2c6d396e0..22af2f5cf 100644 --- a/tests/frontend/with-page-auth-required.test.tsx +++ b/tests/frontend/with-page-auth-required.test.tsx @@ -9,22 +9,17 @@ import { fetchUserUnsuccessfulMock, fetchUserErrorMock, withUserProvider, user } import { withPageAuthRequired } from '../../src/frontend'; const windowLocation = window.location; -const routerMock: { - basePath?: string; - asPath: string; -} = { - basePath: undefined, - asPath: '/' -}; - -jest.mock('next/router', () => ({ useRouter: (): any => routerMock })); describe('with-page-auth-required csr', () => { beforeAll(() => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore window.location is non-optional delete window.location; - window.location = { ...windowLocation, assign: jest.fn() }; + window.location = { + ...windowLocation, + assign: jest.fn(), + toString: jest.fn(() => 'https://example.com') + }; }); afterEach(() => delete (global as any).fetch); afterAll(() => (window.location = windowLocation)); @@ -86,47 +81,56 @@ describe('with-page-auth-required csr', () => { await waitFor(() => expect(screen.getByText('Error')).toBeInTheDocument()); }); - it('should accept a returnTo url', async () => { + it('should use a custom login URL', async () => { + process.env.NEXT_PUBLIC_AUTH0_LOGIN = '/api/foo'; (global as any).fetch = fetchUserUnsuccessfulMock; const MyPage = (): JSX.Element => <>Private; - const ProtectedPage = withPageAuthRequired(MyPage, { returnTo: '/foo' }); + const ProtectedPage = withPageAuthRequired(MyPage); + + render(, { wrapper: withUserProvider() }); + await waitFor(() => expect(window.location.assign).toHaveBeenCalledWith(expect.stringContaining('/api/foo'))); + delete process.env.NEXT_PUBLIC_AUTH0_LOGIN; + }); + + it('should return to the root path', async () => { + window.location.toString = jest.fn(() => 'https://example.net'); + (global as any).fetch = fetchUserUnsuccessfulMock; + const MyPage = (): JSX.Element => <>Private; + const ProtectedPage = withPageAuthRequired(MyPage); render(, { wrapper: withUserProvider() }); await waitFor(() => expect(window.location.assign).toHaveBeenCalledWith( - expect.stringContaining(`?returnTo=${encodeURIComponent('/foo')}`) + expect.stringContaining(`?returnTo=${encodeURIComponent('/')}`) ) ); }); - it('should use a custom login url', async () => { - process.env.NEXT_PUBLIC_AUTH0_LOGIN = '/api/foo'; + it('should return to the current path', async () => { + window.location.toString = jest.fn(() => 'https://example.net/foo'); (global as any).fetch = fetchUserUnsuccessfulMock; const MyPage = (): JSX.Element => <>Private; const ProtectedPage = withPageAuthRequired(MyPage); render(, { wrapper: withUserProvider() }); - await waitFor(() => expect(window.location.assign).toHaveBeenCalledWith(expect.stringContaining('/api/foo'))); - delete process.env.NEXT_PUBLIC_AUTH0_LOGIN; + await waitFor(() => + expect(window.location.assign).toHaveBeenCalledWith( + expect.stringContaining(`?returnTo=${encodeURIComponent('/foo')}`) + ) + ); }); - it('should prepend the basePath to the returnTo URL', async () => { - const asPath = routerMock.asPath; - const basePath = routerMock.basePath; - routerMock.basePath = '/foo'; - routerMock.asPath = '/bar'; + it('should accept a custom returnTo URL', async () => { (global as any).fetch = fetchUserUnsuccessfulMock; const MyPage = (): JSX.Element => <>Private; - const ProtectedPage = withPageAuthRequired(MyPage); + const ProtectedPage = withPageAuthRequired(MyPage, { returnTo: '/foo' }); render(, { wrapper: withUserProvider() }); await waitFor(() => expect(window.location.assign).toHaveBeenCalledWith( - expect.stringContaining(`?returnTo=${encodeURIComponent('/foo/bar')}`) + expect.stringContaining(`?returnTo=${encodeURIComponent('/foo')}`) ) ); - routerMock.basePath = basePath; - routerMock.asPath = asPath; }); it('should preserve multiple query params in the returnTo URL', async () => {