Skip to content

Instantly share code, notes, and snippets.

@TheRusskiy
Last active May 10, 2021 08:35
Show Gist options
  • Save TheRusskiy/7e5404a8a12896efb79b8ec7848c5dbb to your computer and use it in GitHub Desktop.
Save TheRusskiy/7e5404a8a12896efb79b8ec7848c5dbb to your computer and use it in GitHub Desktop.
Restrict Next.js page to authenticated users
const ProfilePage: NextPage = () => {
return (
<PageLayout>
{/* PAGE BODY */}
</PageLayout>
)
}
// THE ACTUAL USAGE
requireAuth(ProfilePage)
export default ProfilePage
// 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
}
}
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