Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to protect routes in Next.js next-auth?

I am trying to integrate authentication with next-auth library in an Application. I have been following the official tutorial given here https://github.com/nextauthjs/next-auth-example/. The problem with the given example is that I need to check if there is a session on every page that requires authentication like this.

    import { useState, useEffect } from 'react';
    import { useSession } from 'next-auth/client'
    
    export default function Page () {
      const [ session, loading ] = useSession()
      
      // Fetch content from protected route
      useEffect(()=>{
        const fetchData = async () => {
          const res = await fetch('/api/examples/protected')
          const json = await res.json()
        }
        fetchData()
      },[session])
    
      // When rendering client side don't display anything until loading is complete
      if (typeof window !== 'undefined' && loading) return null
    
      // If no session exists, display access denied message
      if (!session) { return  <Layout><AccessDenied/></Layout> }
    
      // If session exists, display content
      return (
        <Layout>
          <h1>Protected Page</h1>
          <p><strong>{content || "\u00a0"}</strong></p>
        </Layout>
      )
    }

or like this for Server-side checking

    import { useSession, getSession } from 'next-auth/client'
    import Layout from '../components/layout'
    
    export default function Page () {
      // As this page uses Server Side Rendering, the `session` will be already
      // populated on render without needing to go through a loading stage.
      // This is possible because of the shared context configured in `_app.js` that
      // is used by `useSession()`.
      const [ session, loading ] = useSession()
    
      return (
        <Layout>
          <h1>Server Side Rendering</h1>
          <p>
            This page uses the universal <strong>getSession()</strong> method in <strong>getServerSideProps()</strong>.
          </p>
          <p>
            Using <strong>getSession()</strong> in <strong>getServerSideProps()</strong> is the recommended approach if you need to
            support Server Side Rendering with authentication.
          </p>
          <p>
            The advantage of Server Side Rendering is this page does not require client side JavaScript.
          </p>
          <p>
            The disadvantage of Server Side Rendering is that this page is slower to render.
          </p>
        </Layout>
      )
    }
    
    // Export the `session` prop to use sessions with Server Side Rendering
    export async function getServerSideProps(context) {
      return {
        props: {
          session: await getSession(context)
        }
      }
    }

This is a lot of headaches as we need to manually right on every page that requires auth, Is there any way to globally check if the given route is a protected one and redirect if not logged in instead of writing this on every page?

like image 513
hafiz muhammed Avatar asked May 16 '21 19:05

hafiz muhammed


People also ask

How do I protect a page in Nextjs?

you can make the authentication of user on server-side, if a user is logged in then show them the content of the protected route else redirect them to some other route.

Is next Auth secure?

NextAuth. js is designed as a secure, confidential client and implements a server side authentication flow.


1 Answers

Yes you need to check on every page and your logic is okay ( showing spinner untll the auth state is available) however,you can lift authentication state up, so you don't repeat the auth code for every page, _app component is a perfect place for this, since it naturally wraps all other components (pages).

      <AuthProvider>
        {/* if requireAuth property is present - protect the page */}
        {Component.requireAuth ? (
          <AuthGuard>
            <Component {...pageProps} />
          </AuthGuard>
        ) : (
          // public page
          <Component {...pageProps} />
        )}
      </AuthProvider>

AuthProvider component wraps logic for setting up third party providers (Firebase, AWS Cognito, Next-Auth)

AuthGuard is the component where you put your auth check logic. You will notice that AuthGuard is wrapping the Component (which is the actual page in Next.js framework). So AuthGuard will show the loading indicator while querying the auth provider, and if auth is true it will show the Component if auth is false, it could show a login popup or redirect to the login page.

About Component.requireAuth this is a handy property that is set on every page to mark the Component as requiring auth, if that prop is false AuthGuard is never rendered.

I've written about this pattern in more detail: Protecting static pages in Next.js application

And I've also made an example demo app (source)

like image 179
Ivan V. Avatar answered Oct 04 '22 21:10

Ivan V.