Reputation: 466
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
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
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
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
Reputation: 1
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
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
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
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