Having trouble with Next-Auth's signIn with Credentials feature in NextJS?

I have recently added the next-auth package to my new Next.js project. Despite following all the documentation for both Next.js and next-auth, I am still unable to resolve the issue.

The problem I am encountering is as follows: I am trying to log in to my Next.js application using an Email & Password combination submitted to my API Server built on Laravel. Upon submitting the login form, I am running the function below:

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

The code snippet shown below is present 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!');
              });
          },
        }),
    ],
});

However, when attempting to log in with valid/invalid credentials, I receive the following error in Google Chrome's console log:

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

Is there something that I might be overlooking here?

Answer №1

According to the information provided (https://next-auth.js.org/providers/credentials#example)

async authorize(credentials, req) {
  // Implement your custom logic here to retrieve user data based on the credentials
  const user = { id: 1, name: 'John Doe', email: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="acb8b7afb9aae4bbbdb988b5bfb3cbacbb">[email protected]</a>' }

  if (user) {
    // Any object returned will be stored in the JWT's `user` property
    return user
  } else {
    // Return null or false to reject the credentials
    return null
    // You can also trigger an Error or specify a URL for rejection:
    // throw new Error('error message') // Redirect to error page
    // throw '/path/to/redirect'        // Redirect to a URL
  }
}

Ensure that you are returning either a user object or null from the authorize callback.

Answer №2

shanewwarren's initial answer is accurate, but allow me to provide a more detailed explanation:

To tackle this issue, we can utilize the axios library.

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

Answer №3

Consider including a try/catch block within the async function authorize(credentials) {} to handle errors more effectively. In my experience, adding some logging statements was instrumental in troubleshooting an issue that ultimately required switching to a different Node.js version.

Answer №4

I encountered the same error, but found a solution that 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: "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="5b2f3e282f1b3e233a362b373e75383436">[email protected]</a>" },
        password: { label: "Password", type: "password" },
      },
      async authorize(credentials, req) {
        // const user1 = { id: 1, name: 'J Smith', email: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="600a130d091408200518010d100c054e030f0d">[email protected]</a>' }
        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>
  );
}

Answer №5

Dealing with a similar issue, I encountered some challenges.

To resolve it, I included a `try/catch` block in my fetch request and utilized a console.log() statement within the catch block to track errors effectively.

In my case, the root cause of the problem stemmed from a

self-signed certificate

To address this issue, I implemented NODE_TLS_REJECT_UNAUTHORIZED=0 in my .env.local file, efficiently resolving the error.

Answer №6

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

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

        userInfo = await axios.post("https://fakejson.com/auth/login", {
          email: credentials.email,
          password: credentials.password,
        });

        if (userInfo) {
          return userInfo.data;
        }

        return null;
      },
    }),
  ],
} meets requirements of NextAuthConfig;

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Angular - connecting a function directly

Is there any potential performance impact of directly binding a function in directives like ng-show in AngularJS? <div ng-show="myVm.isVisible()"> .... </div> // controller snippet (exposed through controllerAs syntax) function myCtrl (myServ ...

What is the best way to implement automatic scrolling to the bottom of a materialUI drawer in React after a page refresh?

I am facing an issue with my Material UI Drawer, which contains numerous checkboxes and radio buttons for an advanced search page. Whenever a checkbox or radio button is clicked, it triggers an API request to fetch results instantly without requiring a sub ...

Create dynamic cells for CSS grid using JavaScript

I have been manually generating grid cells in a specific pattern by copying and adjusting <div> elements. Although this method works, I am interested in creating an algorithm that can automatically generate the desired layout. The left box in the exa ...

Utilizing Jquery Validation to Remove a Class Upon Form Validation Success

In my current registration process, I have a multipart form where each subsequent form is displayed when the next button is pressed without fading effects. Initially, the button appears faded. Here's a simplified version of how I handle the first form ...

Building Dynamic Props in Vue.js using v-select Component

I am utilizing a chart that relies on properties for data. <template> <v-row> <v-col col="12" sm="12"> <Chart :data="series2"></Chart> ### This chart receives the props < ...

Starting a new React project

prtScn I'm encountering an issue while trying to initiate a new react app in Visual Studio Code. I have been following the given instructions as follows: in the visual code terminal : create-react-app sali (sali is the name) npm install node node_m ...

How can I stop TypeScript from causing my builds to fail in Next.js?

Encountering numerous type errors when executing yarn next build, such as: Type error: Property 'href' does not exist on type '{ name: string; }'. This issue leads to the failure of my build process. Is there a specific command I can ...

Using the setTimeout function in Vue.js to trigger the play of an audio file at a specified

I have set up a Vue-audio player in my Vue.js application using the AudioPlayer component. This is my code: <template> <vue-audio id = "myAudio" :file= "file1"/> </template> <script> import VueAudio from 'vue-audio'; ...

Attempting to store the output of a function in a variable

My current script is designed to verify if the value of a selected element matches the span id. While the function itself functions correctly (verifying object.id via alert), I am encountering issues with variable assignment. When attempting to alert the ...

Generating a highchart visualizing a pie chart using data from a local JSON file (OBJECT)

This is the JSON data from my JSON file {"diskspace":100,"diskspace.free":50,"time":8,"time.played":2,"controllers":25,"controllers.used":3, "controllers.new":10, "controllers.broken":12} I want to represent this JSON data in various forms of pie cha ...

VS code showing live server as installed but failing to function properly

After installing the live server extension, I noticed that my browser is not updating when I save my HTML or other files. What could be causing this issue? ...

Redirect URL problem in the PayPal login function

After setting up the URL as http://127.0.0.1:3000/link-paypal on my profile and dashboard, I encountered an issue when trying with localhost. The system prompted me to try again, for reasons unknown, hence now switching to 127... In addition, I replaced t ...

How to assign a value in an HTML element using AngularJS

Currently, I am utilizing Angular JS to iterate through a portion of a scope that is initially a JSON array. My objective is to verify the existence of a specific value in that array. If the value exists, then certain actions should be taken. The code bel ...

modify the final attribute's value

Hello I've been using skrollr js to create a parallax website. However, the issue arises when determining the section height, as it depends on the content within. My goal is to locate the last attribute and adjust the number value based on the section ...

Obtain the Key with the Greatest Value from a JSON Object

I'm currently working with data fetched from an API response in an attempt to identify the pitch with the highest speed. Below is a snippet from the API response. { page: 1, total_pages: 4, listings: [ { name: "Zack Greinke", pitc ...

Loading JSON data from a file in an AngularJS service

Looking to retrieve JSON data from a file named driverStandings.json, which can be found in the structure from the provided image. I am utilizing a service API to fetch this information by using the code displayed below (refer to image). After compiling, ...

Where can I locate the Socket.IO server within the local area network (LAN)?

I am currently in the process of developing an application that facilitates connections between devices within the same network. In my design, any device can act as a server, and I aim for clients to automatically detect the server without requiring users ...

In Javascript, have an audio clip play every 30 seconds

My webpage has a unique feature - it automatically plays a short audio clip as soon as the page loads. This functionality is achieved using a special audio player called nifty player. Here is the code snippet for embedding the player: <object classid= ...

Testing a subordinate function within the main method that involves HTTP request authorization

Dealing with a tricky situation here, hoping for some guidance. So I'm working with a main method that serves as an api endpoint. This method calls another function to check if the user is authorized to access this endpoint or not. The sub-method, r ...

Is it possible to implement multer in Next.js without using Express?

import type { NextApiRequest, NextApiResponse } from 'next' import multer from 'multer' const upload = multer({ dest: 'public/uploads/' }) export default async (req: any, res: NextApiResponse) => { // Implementing switch ...