Search code examples
reactjsnext.jsnext-auth

Next-Auth implementation not working properly. getting 404 error everywhere


I am trying to implement authentication using Nex-Auth from the fetch API and getting 404 errors everywhere.

the error I got enter image description here

My folder structure enter image description here

layout.tsx

import { Inter } from "next/font/google";
import "./globals.css";
import { ReactNode } from "react";
import AuthProvider from "./pages/_app";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

type Props = {
  children: ReactNode;
};

export default function RootLayout({ children }: Props) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <AuthProvider>
          {children}
        </AuthProvider>
      </body>
    </html>
  );
}

protected.tsx


function ProtectedPage() {
  return <div>This is a protected page</div>;
};

export default withAuth(ProtectedPage);

_app.tsx

import { SessionProvider } from 'next-auth/react';

type Props = {
  children?: React.ReactNode;
};

export const AuthProvider = ({ children }: Props) => {
  return <SessionProvider>{children}</SessionProvider>;
};

export default AuthProvider;

signin.tsx

import { signIn } from 'next-auth/react';
import { useState } from 'react';
import { useRouter } from 'next/router';

const SignIn = () => {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const router = useRouter();

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    const result = await signIn('credentials', {
      redirect: false,
      username,
      password,
    });

    if (result?.ok) {
      router.push('/');
    } else {
      // handle error
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>Username</label>
        <input type="text" value={username} onChange={(e) => setUsername(e.target.value)} />
      </div>
      <div>
        <label>Password</label>
        <input type="password" value={password} onChange={(e) => setPassword(e.target.value)} />
      </div>
      <button type="submit">Sign In</button>
    </form>
  );
};

export default SignIn;

login.ts

import { NextApiRequest, NextApiResponse } from 'next';

const loginHandler = (req: NextApiRequest, res: NextApiResponse) => {
  if (req.method === 'POST') {
    const { username, password } = req.body;
    // Add your authentication logic here
    if (username === 'admin' && password === 'admin') {
      res.status(200).json({ id: 1, name: 'Admin' });
    } else {
      res.status(401).json({ error: 'Invalid credentials' });
    }
  } else {
    res.setHeader('Allow', ['POST']);
    res.status(405).end(`Method ${req.method} Not Allowed`);
  }
};

export default loginHandler;

[...nextauth].ts

import CredentialsProvider from 'next-auth/providers/credentials';

export default NextAuth({
  providers: [
    CredentialsProvider({
      name: 'Credentials',
      credentials: {
        username: { label: 'Username', type: 'text' },
        password: { label: 'Password', type: 'password' },
      },
      async authorize(credentials) {
        // Replace this with your own fetch API call
        const res = await fetch('https://your-api-url.com/login', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify(credentials),
        });

        const user = await res.json();

        if (res.ok && user) {
          return user;
        } else {
          return null;
        }
      },
    }),
  ],
  pages: {
    signIn: '/auth/signin',
  },
});

withAuth.tsx

import { useSession } from 'next-auth/react';
import { useRouter } from 'next/router';
import { ComponentType, useEffect } from 'react';

const withAuth = <P extends object>(WrappedComponent: ComponentType<P>) => {
  const Wrapper = (props: P) => {
    const { data: session, status } = useSession()
    const loading = status === "loading"
    const router = useRouter();

    useEffect(() => {
      if (!loading && !session) {
        router.push('/auth/signin');
      }
    }, [loading, session, router]);

    if (loading || !session) {
      return <div>Loading...</div>;
    }

    return <WrappedComponent {...props} />;
  };

  return Wrapper;
};

export default withAuth;

Got this sample code from somewhere in the internet.

Need to understand where did I missed. i couldn't even access any single page like protected.tsx (localhost:300/protected).

Also, how to access login page and get the session id from api.


Solution

  • Finally, I found it by myself. I was thinking of deleting this question but it may be helpful for someone who struggled.

    Actually, following Nextjs folder structures is important.

    enter image description here

    Read here

    in my folder structure, I kept the pages folder inside the app folder. But the root folder is src. So nextjs will ignore and consider the nested folder as other functionalities rather than assuming it as a route.