Skip to content

Commit

Permalink
Merge pull request #45 from charamza/master
Browse files Browse the repository at this point in the history
Feat/bypass protection for specified routes
  • Loading branch information
BJvdA authored Jun 16, 2022
2 parents 15ba0ac + 93c7ae7 commit 34a6834
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ Option | Description | Default value
`loginApiUrl`| Relative path of the api route handled by `loginHandler` | `'/api/login'`
`loginComponent`| Supply your own React component to show as login prompt | `LoginComponent`
`loginComponentProps`| Properties object to customize the login prompt, without overriding the entire component (see below) | `{}`
`bypassProtection`| Bypass protection for specific routes, decided by callback with `NextRouter` param | `({ route }) => false`

The `loginComponentProps` object can contain any of the following options:

Expand Down
49 changes: 49 additions & 0 deletions src/hoc/__tests__/withPasswordProtect.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { rest } from 'msw';
import { setupServer } from 'msw/node';
import * as hooks from 'next/amp';

import * as nextRouter from 'next/router';
import { withPasswordProtect } from '../withPasswordProtect';

const App = ({ Component }) => <Component />;
Expand Down Expand Up @@ -150,4 +151,52 @@ describe('[hoc] withPasswordProtect', () => {
expect(screen.getByText('Password')).toBeInTheDocument();
});
});

it('should be able to bypass protection for specified routes', async () => {
const Wrapped = withPasswordProtect(App, {
bypassProtection: ({ route }) => route === '/bypassed',
});

// ORDINARY PAGE
jest.spyOn(require('next/router'), 'useRouter').mockImplementation(() => ({
route: '/',
}));

await act(async () => {
render(
<Wrapped
Component={() => <div>hidden</div>}
pageProps={{}}
router={
{
route: '/bypassed',
} as nextRouter.Router
}
/>,
);
});

await waitFor(() => {
expect(screen.getByText('Password')).toBeInTheDocument();
});

// BYPASSED PAGE
jest.spyOn(require('next/router'), 'useRouter').mockImplementation(() => ({
route: '/bypassed',
}));

await act(async () => {
render(
<Wrapped
Component={() => <div>hidden</div>}
pageProps={{}}
router={{} as any}
/>,
);
});

await waitFor(() => {
expect(screen.getByText('hidden')).toBeInTheDocument();
});
});
});
6 changes: 5 additions & 1 deletion src/hoc/withPasswordProtect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { ElementType, useEffect, useState } from 'react';
import { useAmp } from 'next/amp';
import type { AppProps } from 'next/app';

import { NextRouter, useRouter } from 'next/router';
import {
LoginComponent as DefaultLoginComponent,
LoginComponentProps,
Expand All @@ -14,6 +15,7 @@ interface PasswordProtectHOCOptions {
loginApiUrl?: string;
loginComponent?: ElementType;
loginComponentProps?: Omit<LoginComponentProps, 'apiUrl'>;
bypassProtection?: (route: NextRouter) => boolean;
}

/// TODO: improve App typing
Expand All @@ -26,6 +28,7 @@ export const withPasswordProtect = (
const [isAuthenticated, setAuthenticated] = useState<undefined | boolean>(
undefined,
);
const router = useRouter();

const checkIfLoggedIn = async () => {
try {
Expand All @@ -51,7 +54,8 @@ export const withPasswordProtect = (
return null;
}

if (isAuthenticated) {
const bypassProtection = options?.bypassProtection?.(router) ?? false;
if (isAuthenticated || bypassProtection) {
return <App Component={Component} pageProps={pageProps} {...props} />;
}

Expand Down

1 comment on commit 34a6834

@vercel
Copy link

@vercel vercel bot commented on 34a6834 Jun 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.