Search code examples
next.jsapp-routernext-auth

NextAuth in app router NEXT.js | Session is undefined


I'm using NextAuth with NEXT.js app router for authentication.

The dashboard component is wrapped in auth-guard which checks whether the user is signed in or not. the auth-guard's code is as follows.

export default function AuthGuard({ children }: Props) {
  const { loading } = useAuthContext();

  return <>{loading ? <SplashScreen /> : 
  <SeshProviders>
  <Container>
    {children}
  </Container>
  </SeshProviders>
  }</>;
}

// ----------------------------------------------------------------------

function Container({ children }: Props) {
  const router = useRouter();

  const { authenticated, method } = useAuthContext();
  const { data: session, status } = useSession();
  
  

  const [checked, setChecked] = useState(false);
  console.log('session from auth-guard', session);
  const check = useCallback(() => {
    if (status !== 'authenticated') {
      const searchParams = new URLSearchParams({
        returnTo: window.location.pathname,
      }).toString();

      const loginPath = loginPaths[method];

      const href = `${loginPath}?${searchParams}`;
      console.log('taking back from auth guard', session);
      router.replace(href);
    } else {
      setChecked(true);
      console.log('auth-guard said authenticated');
    }
  }, [router, status, session]);

  useEffect(() => {
    check();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  if (!checked) {
    return null;
  }

  return <>{children}</>;
}

My next auth config:

// nextauth/auth.ts
export const authOptions: NextAuthOptions = {
  // Configure one or more authentication providers
  session: {
    strategy: 'jwt',
    maxAge: 30 * 24 * 60 * 60, // 30 days
  },
  providers: [
    CredentialsProvider({
      name: 'Credentials',
      credentials: {
        email: { label: 'Email', type: 'text', placeholder: 'jsmith' },
        password: { label: 'Password', type: 'password' },
      },
      async authorize(credentials, req) {
        console.log(credentials);
        if (!credentials) return null;
        const email = credentials.email;
        const password = credentials.password;

        const res = await axios.post(endpoints.auth.login, { email, password });
        if (res.status === 200) {
          const { token, user } = res.data;
          user.token = token;
          return user;
        }
        return null;
      },
    }),
  ],
  secret: process.env.NEXAUTH_SECRET,
  callbacks: {
    async redirect({ url, baseUrl }: { url: string; baseUrl: string }) {
      if (!url) return '/dashboard/user';
      return url;
    },
    async jwt({ token, user }: { token: any; user: any }) {
      if (user) {
        token.accessToken = user.token;
      }
      console.log('jwt', token);
      return token;
    },
    async session({ session, token, user }: { session: any; token: any; user: any }) {
      // Send properties to the client, like an access_token and user id from a provider.
      session.accessToken = token.accessToken;
      session.user.id = token.id;
      console.log('session', session);
      return session;
    },
  },
};

export const getServerAuthSession = () => getServerSession(authOptions);

also app/api/auth/[...nextauth]/route.ts

import { authOptions } from 'src/nextauth/nextauth';



// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };

Now when I login, the router takes me to the dashboard for a second and then takes my back to login. console's result

Any idea what I might be doing wrong? (or if I'm doing anything right)


Solution

  • It is better to use a middleware to handle protected pages. This will also prevent the page from loading until the session is loaded.


    If you really must do this client-side, you have to check that the status is not "loading". The useSession hook makes a request to the auth API provided by next-auth, which will load the session information.

    Your useEffect should also depend on check, since it can change based on your session state. Suppressing that warning is a bug.