skip to Main Content

I am trying to build a website using NextJS v13 and Next Auth v4. Whenever a page/component is loaded, the session is not returned in the first render when using useSession hook and because of this, many components don’t work like they should…

For instance, the following code should load the questions for authenticated users:

'use client'
import { useEffect, useState } from 'react'
import { Question } from '@prisma/client'
import { useSession } from 'next-auth/react'

function QuestionPage() {
  const { data: session } = useSession() // this is null
  const [questions, setQuestions] = useState<Question[]>([])
  const [questionStatus, setQuestionStatus] = useState('');

  useEffect(() => {
    console.log('Session: ', session)
    if (!session) return;

    setQuestionStatus('Loading questions...');

    fetch('/api/questions')
        .then((res) => res.json())
        .then((data) => {
            setQuestions(data.questions)
            setQuestionStatus('');
        })
  }, [])

  return (
    <div>
      <p>{questionStatus}</p>
      {questions && questions.map(question => {
         <p key={question.id}>{question.title}</p>
      })}
    </div>
  )
}

This is my ProviderWrapper component that wraps the main app:

'use client'
import QuestionContextWrapper from '@/context/QuestionContext'
import { SessionProvider } from 'next-auth/react'

export const metadata = {
  title: 'My App',
  description: 'My description',
}

export default function ProvidersWrapper({ children }: { children: React.ReactNode }) {
  return (
    <SessionProvider>
      <QuestionContextWrapper>
        {children}
      </QuestionContextWrapper>
    </SessionProvider>
  )
}

And this is the layout.tsx file:

import './globals.css'
import Header from '@/components/Header'
import ProvidersWrapper from './ProvidersWrapper'
import Footer from '@/components/Footer'

export const metadata = {
  title: 'My app',
  description: 'My description',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        <ProvidersWrapper>
          <Header />
          {children}
          <Footer />
        </ProvidersWrapper>
      </body>
    </html>
  )
}

Anyone knows why is next-auth behaving like this?

2

Answers


  1. Chosen as BEST ANSWER

    After some research and due to the @Thusithz's answer, I have found this guide to implement proper authentication using Next Auth and TypeScript. So I changed my code to the following and it worked.

    I have renamed /src/components/PriverWrapper.tsx to src/context/AuthProvider.tsx and changed the code as well to the following:

    'use client'
    import { Session } from 'next-auth'
    import { SessionProvider } from 'next-auth/react'
    
    export const metadata = {
      title: 'My App',
      description: 'My description',
    }
    
    export interface AuthContextProps {
      children: React.ReactNode
      session: Session
    }
    
    export default function AuthContext({ children }: AuthContextProps) {
      return <SessionProvider>{children}</SessionProvider>
    }
    

    And then applied this to the /src/app/layout.tsx file wrapping the node with AuthProvider and passing the session variable:

    import './globals.css'
    import Header from '@/components/Header'
    import AuthProvider from '@/context/AuthContext'
    import Footer from '@/components/Footer'
    import { Session } from 'next-auth'
    
    async function getSession(cookie: string): Promise<Session> {
      const response = await fetch(`${process.env.NEXTAUTH_URL}/session`, {
        headers: {
          cookie,
        }
      })
    
      const session = await response.json()
    
      return Object.keys(session).length > 0 ? session : null
    }
    
    export const metadata = {
      title: 'My app',
      description: 'My description',
    }
    
    export default function RootLayout({
      children,
    }: {
      children: React.ReactNode
    }) {
      const session = await getSession(headers().get('cookie') ?? '');
      return (
        <html lang="en">
          <body>
            <AuthContext session={session}>
              <Header />
              {children}
              <Footer />
            </AuthContext >
          </body>
        </html>
      )
    }
    

    So, this works as expeted.


  2. Initial load session will be undefined since it’s not pass to provider. Try to pass existing session to provider like below.

    export default function ProvidersWrapper({ children, session }: { children: React.ReactNode }) {
      return (
        <SessionProvider baseUrl={/* BASE_URL */} refetchInterval={60 * 5} session={session}>
          <QuestionContextWrapper>{children}</QuestionContextWrapper>
        </SessionProvider>
      );
    }
    
    ProvidersWrapper.getInitialProps = async (context) => {
      const { ctx } = context;
      const session = await getSession(ctx);
    
      return {
        session,
      };
    };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search