Lalith Kumar
Lalith Kumar

Reputation: 466

Next-Auth signIn with Credentials is not working in NextJS

I'm integrating next-auth package to my fresh Next.js project. I have followed all of the Next.js and next-auth documentations but not able to find a solution.

The issue I'm facing goes like this: I want to Login to my Next.js app using Email & Password submitted to my API Server running on Laravel. When submitting the login form I'm executing the below function.

import { signIn } from "next-auth/client";

const loginHandler = async (event) => {
    event.preventDefault();

    const enteredEmail = emailInputRef.current.value;
    const enteredPassword = passwordInputRef.current.value;

    const result = await signIn("credentials", {
        redirect: false,
        email: enteredEmail,
        password: enteredPassword,
    });

    console.log("finished signIn call");
    console.log(result);
};

And code shown below is in my pages/api/auth/[...nextauth].js

import axios from "axios";
import NextAuth from "next-auth";
import Providers from "next-auth/providers";

export default NextAuth({
    session: {
        jwt: true,
    },
    providers: [
        Providers.Credentials({
          async authorize(credentials) {
            axios
              .post("MY_LOGIN_API", {
                email: credentials.email,
                password: credentials.password,
              })
              .then(function (response) {
                console.log(response);
                return true;
              })
              .catch(function (error) {
                console.log(error);
                throw new Error('I will handle this later!');
              });
          },
        }),
    ],
});

But when try to login with correct/incorrect credentials, I get the below error in Google Chrome console log.

POST http://localhost:3000/api/auth/callback/credentials? 401 (Unauthorized)
{error: "CredentialsSignin", status: 401, ok: false, url: null}

Am I missing something here?

Upvotes: 17

Views: 52224

Answers (7)

Ashura
Ashura

Reputation: 1

Try adding the custom error to check where you are going wrong, as I am trying to log in with the wrong password and when I throw custom errors I get the password not match error. Worked for me.

      async authorize(credentials, req) {
        if (!credentials?.email || !credentials?.password) {
          throw new Error("Invalid credentials");
        }
        const existingUser = await db.user.findUnique({
          where: { email: credentials?.email },
        });
        if (!existingUser) {
          throw new Error(`User ${credentials?.email} not found`);
        }
        const passwordMatch = await compare(
          credentials?.password,
          existingUser.password
        );
        if (!passwordMatch) {
          throw new Error(`User ${credentials?.email} not macth`);
        }
        return {
          id: `${existingUser.id}`,
          username: existingUser.username,
          email: existingUser.email,
        };
      },
   

Upvotes: 0

Yoko Hailemariam
Yoko Hailemariam

Reputation: 47

import type { NextAuthConfig } from "next-auth";
import Credentials from "next-auth/providers/credentials";
import axios from "axios";

export default {
  providers: [
    Credentials({
      credentials: {
        username: {},
        password: {},
      },
      authorize: async (credentials) => {
        let user = null;

        user = await axios.post("https://dummyjson.com/auth/login", {
          username: credentials.username,
          password: credentials.password,
        });

        if (user) {
          return user.data;
        }

        return null;
      },
    }),
  ],
} satisfies NextAuthConfig;

Upvotes: 0

Darkerone
Darkerone

Reputation: 101

I had the same problem.

Add a try/catch to your fetch request and a console.log() in the catch. You will see errors in terminal.

For me, the problem was a

self-signed certificate

error that I solved adding NODE_TLS_REJECT_UNAUTHORIZED=0 in .env.local file

Upvotes: 0

I was getting same error as well. this worked :

export const authOptions = {
  secret: process.env.SECRET,
  adapter: MongoDBAdapter(clientPromise),
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    }),
    CredentialsProvider({
      name: 'Credentials',
      id: 'credentials',
      credentials: {
        username: { label: "Email", type: "email", placeholder: "[email protected]" },
        password: { label: "Password", type: "password" },
      },
      async authorize(credentials, req) {
        // const user1 = { id: 1, name: 'J Smith', email: '[email protected]' }
        const email = credentials?.email;
        const password = credentials?.password;

        mongoose.connect(process.env.MONGO_URL);
        const user = await User.findOne({email});
        const passwordOk = user && bcrypt.compareSync(password, user.password);
        // if (user1) {
        //   return user1;
        // }
        if (passwordOk) {
          return user;
        }

        return null
      }
    })
  ],
  session: {
    strategy: 'jwt'
  },
  secret: process.env.SECRET,
  // callbacks: {
  //   async jwt(token, user) {
  //     if (user) {
  //       console.log(user,"user jwt");
  //       token.id = user.id;
  //       token.name = user.name;
  //     }
  //     console.log(token,"token jwt");
  //     return token;
  //   },
  // },
  // callbacks: {
  //   async session(session, token) {
  //     // if you need to add more info in session
  //     console.log(token,"token session");
  //     session.user.id = token.id;
  //     session.user.name = token.name;
  //     console.log(session,"session");
  //     return session;
  //   },
  // },
};
const handler = NextAuth(authOptions);

export { handler as GET, handler as POST }

Login:

"use client";
import { signIn } from "next-auth/react";
import Image from "next/image";
import { useState } from "react";

export default function LoginPage() {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [loginInProgress, setLoginInProgress] = useState(false);

  async function handleFormSubmit(ev) {
    ev.preventDefault();
    setLoginInProgress(true);

    await signIn("credentials", { email, password, callbackUrl: "/" });

    setLoginInProgress(false);
  }
  return (
    <section className="mt-8">
      <h3 className="text-center text-primary font-extrabold text-4xl mb-4">
        Login
      </h3>
      <form className="max-w-xs mx-auto" onSubmit={handleFormSubmit}>
        <input type="email" name="email" placeholder="email" value={email}
               disabled={loginInProgress}
               onChange={ev => setEmail(ev.target.value)} />
        <input type="password" name="password" placeholder="password" value={password}
               disabled={loginInProgress}
               onChange={ev => setPassword(ev.target.value)}/>
        <button disabled={loginInProgress} type="submit">Login</button>
      </form>
    <hr className="mt-6"/>
      <div className="max-w-xs mx-auto">
        <div className="my-4 text-center text-gray-500">
          login with provider
        </div>
        <button
          type="button"
          onClick={() => signIn("google", { callbackUrl: "/" })}
          className="flex gap-4 justify-center"
        >
          <Image src={"/google.png"} alt={""} width={24} height={24} />
          Login with google
        </button>
      </div>
    </section>
  );
}

Upvotes: 0

Joseph
Joseph

Reputation: 11

add try/cath inside async authorize(credentials) {} put some log there, in my case, I check the log and fixed the problem by using other node version

Upvotes: 1

Sohel Shekh
Sohel Shekh

Reputation: 351

Answer posted by shanewwarren is correct, but here is more elaborated answer,

Using axios to solve this

 async authorize(credentials, req) {
        return axios
          .post(`${process.env.NEXT_PUBLIC_STRAPI_API}/auth/login`, {
            identifier: credentials.identifier,
            password: credentials.password,
          })
          .then((response) => {
            return response.data;
          })
          .catch((error) => {
            console.log(error.response);
            throw new Error(error.response.data.message);
          }) || null;
      },

Upvotes: 5

shanewwarren
shanewwarren

Reputation: 2294

From the documentation (https://next-auth.js.org/providers/credentials#example)

async authorize(credentials, req) {
  // Add logic here to look up the user from the credentials supplied
  const user = { id: 1, name: 'J Smith', email: '[email protected]' }

  if (user) {
    // Any object returned will be saved in `user` property of the JWT
    return user
  } else {
    // If you return null or false then the credentials will be rejected
    return null
    // You can also Reject this callback with an Error or with a URL:
    // throw new Error('error message') // Redirect to error page
    // throw '/path/to/redirect'        // Redirect to a URL
  }
}

You are not currently returning a user or null from the authorize callback.

Upvotes: 11

Related Questions