17

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?

8 Answers 8

11

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Can you explain why the status in the response from the next API call, always comes as 200 and ok value is also always true? This doesn't make any sense.
5

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;
      },

Comments

1

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

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
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>
  );
}

1 Comment

Welcome to StackOverflow! I'm not sure how this code dump will help others. Could you try to make a minimal example explaining the fix?
0

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

Comments

0

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,
        };
      },
   

Comments

0

Hi, all!

In my case, the issue occurred after deployed the project to the production environment. I resolved it by adjusting the AUTH_URL variable in the .env file as follows:

// .env file

// dev environment default value
AUTH_URL=http://localhost:3000/api/auth

// replace with the following value for production environment:
AUTH_URL=https://{your-domain}/api/auth

*remenber to replace {your-domain} with your production domain.

This change ensured that the authentication process was pointing to the correct production URL. I hope this helps anyone encountering a similar problem! Let me know if you have any questions.

Regards,

Comments

-1
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;

1 Comment

Your contribution is appreciated, but please provide an explanation for your code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.