Last active
May 10, 2021 08:35
-
-
Save TheRusskiy/7e5404a8a12896efb79b8ec7848c5dbb to your computer and use it in GitHub Desktop.
Restrict Next.js page to authenticated users
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const ProfilePage: NextPage = () => { | |
return ( | |
<PageLayout> | |
{/* PAGE BODY */} | |
</PageLayout> | |
) | |
} | |
// THE ACTUAL USAGE | |
requireAuth(ProfilePage) | |
export default ProfilePage |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// return an augmented `fetch` function that correctly sends | |
// and receives cookies during SSR | |
function makeHttpClient(ctx: NextPageContext): typeof fetch { | |
return async function fetchWithCookies( | |
input: RequestInfo, | |
options: RequestInit = {} | |
) { | |
const actualOptions: RequestInit = { | |
credentials: 'include', // send cookies with request | |
redirect: 'manual', // don't follow redirects | |
...options, | |
headers: { | |
cookie: ctx?.req?.headers?.cookie ?? '', | |
...(options.headers ?? {}) | |
} | |
} | |
// on server side we use isomorphic-unfetch NPM package, | |
// on client side we use browser built-in `fetch` function | |
let isomorphicFetch: typeof fetch | |
if (typeof window === 'undefined') { | |
isomorphicFetch = (await import('isomorphic-unfetch')).default | |
} else { | |
isomorphicFetch = window.fetch | |
} | |
const result = await isomorphicFetch(input, actualOptions) | |
// browsers (client-size) already handle this case automatically, | |
// but in case some cookies are getting set in response, | |
// we need to handle that use case on server-side as well | |
if (ctx?.res) { | |
const cookiesFromApi = result.headers.get('set-cookie') | |
if (cookiesFromApi) { | |
ctx?.res.setHeader('set-cookie', cookiesFromApi) | |
} | |
} | |
return result | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { NextPage, NextPageContext } from 'next' | |
import Router from 'next/router' | |
const requireAuth = (page: NextPage) => { | |
// make sure this function is safe run several times | |
if (page.__authIsRequired) { | |
return | |
} | |
page.__authIsRequired = true | |
const originalGetInitialProps = page.getInitialProps | |
page.getInitialProps = async (ctx: NextPageContext) => { | |
const { res, req } = ctx | |
// httpClient on server side needs to be smart enough to send cookies | |
const fetchWithCookies = makeHttpClient(ctx) | |
const user = await fetchWithCookies('/api/users/current') | |
if (!user) { | |
if (res) { | |
const loginUrl = `/login?redirectTo=${encodeURIComponent(req.url)}` | |
res.writeHead(302, 'Not authenticated', { Location: loginUrl }) | |
res.end() | |
} else { | |
const loginUrl = `/login?redirectTo=${encodeURIComponent( | |
window.location.pathname | |
)}` | |
await Router.push(loginUrl) | |
} | |
return {} | |
} | |
return originalGetInitialProps ? originalGetInitialProps(ctx) : {} | |
} | |
} | |
export default requireAuth | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment