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?
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: 'jsmith#example.com' }
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.
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;
},
Related
Node.js CODE
exports.user = async (req, res) => {
try {
const { wallet } = req.body;
if (!wallet) {
res.status(400).json({ error: "Not logged in" });
return;
} else {
user = User.findone(wallet);
// if user is not found then create a new user and mark as loggged In
if (!user) {
User.create({
user: wallet,
});
}
// if user found then create a session token and mark as logged
in
res.send({
user: wallet,
});
}
} catch (error) {
console.log(`ERROR::`, error);
}
};
REACTJs CODE
// post call/update
const axiosCall = async () => {
// core login will give a unique username by fulling a transcation
// core.login i dont have any control
const userAccount = await core.login();
try {
const res = await Axios.post(`${API}/user`, userAccount, dataToken);
setData({
...data,
error: "",
success: res.data.message,
});
} catch (error) {
setData({
...data,
error: error.response.data.error,
});
}
};
Now here the problem occurs when some one could modify userAccount in the front-end or someone could send a body with wallet: anything to my route localhost:3000/api/user
There is no option for me to check if some actually used core.login(); to get the wallet address.
So is there any solution?
I was thinking to allow only my server IP or localhost to hit the route localhost:3000/api/user and is that even possible?
Also there is another issue anyone could modify userAccount in front-end.
I am working on a Next.js project, in which I have included a login system with NextAuth.
Everything was working fine at the beginning, but recently I keep getting an error every time I try to get the session.
The Error:
https://pastebin.com/Mh624N3c
StackOverflow doesn't let me post the whole error, so I had to use Pastebin.
This is the first time I encounter such an error, and I can't seem to be able to find a solution. I am using JWT as the session strategy, if that has to do anything with the issue.
This is the code I use for handling the authentication & session:
await NextAuth(req, res, {
adapter: MongoDBAdapter(clientPromise),
pages: {
signIn: "/login"
},
providers: [
CredentialsProvider({
name: "credentials",
credentials: {
email: { label: "Email", type: "email", placeholder: "example#email.com" },
password: { label: "Password", type: "password" }
},
async authorize(credentials, req) {
const account = await Accounts.exists(credentials.email)
const cryptr = new Cryptr(Config.secret)
const encEmail = cryptr.encrypt(credentials.email)
const url = process.env.NODE_ENV == "development" ? "http://localhost:3000/" : Config.url
if (account) {
const password = cryptr.decrypt(account.password)
if (credentials.password == password) {
return {
...account,
_id: null
}
} else {
return res.redirect("/login?error=true")
}
} else {
const code = await Accounts.requestVerification(credentials.email, password)
const message = {
to: credentials.email,
from: "noreply#bytestobits.dev",
subject: "BytesToBits API - Create Your Account",
html: emailStyle(credentials?.email, url, code),
}
SendGrid.send(message).then(() => console.log("Email Sent"))
return res.redirect("/verify?email=" + encEmail)
}
}
})
],
jwt: {
secret: Config.secret,
encryption: true
},
secret: Config.secret,
session: {
strategy: "jwt"
},
callbacks: {
async jwt({ token, user }) {
if (user) {
token.user = user
}
return token
},
async session({ session, token }) {
let data = token.user
if (data) {
if (await Accounts.exists(data.email)) {
data.token = await Accounts.getToken(data.email)
}
data.tokenInfo = await Accounts.tokenInfo(data.token)
}
return data
}
}
})
This happens every time I try to fetch the session or authenticate.
When the user authenticates, a session must be formed, which can be fetched from the client for usage. However, whenever I try to authenticate of fetch the session, a "Parse Error: Header overflow" occurs.
I managed to fix the issue! Turns out the Session object was way too long and caused this error.
Basically in the data's tokenInfo field, it had a really long array. So removing that specific field fixed the issue!
When i try to login using Talend API Tester, getting this into terminal:
API resolved without sending a response for /api/auth/callback/credentials, this may result in stalled requests.
Besides, here is the image of Request:
I followed this: Next-Auth.js -> Rest API
Why I'm getting this kind of response & warning?
Below is the [..nextauth.js] file's code. What's wrong with my code?
import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'
import prisma from '../../../lib/prisma'
const options = {
providers: [
Providers.Credentials({
name: 'Credentials',
credentials: {
email: { label: "Email", type: "email", placeholder: "something#example.com" },
password: { label: "Password", type: "password" }
},
async authorize(credentials) {
const {email, password} = credentials
const user = await prisma.user.findFirst({ where: { email, password } })
console.log(user);
// If no error and we have user data, return it
if (user) {
return user
}
// Return null if user data could not be retrieved
return null
}
})
]
}
export default async function handle(req, res) {
console.log('I see');
NextAuth(req, res, options)
}
Note: Actually I don't want to send csrfToken in the body. Only email & password to the endpoint. Please have a look into my this question How to do authentication using NextAuth.js without login page (using postman)
I'm using nuxt to develop a client for my laravel project.
In the login.vue component I have the following JS code
import Form from 'vform'
export default {
head () {
return { title: this.$t('login') }
},
data: () => ({
form: new Form({
email: '',
password: ''
}),
remember: false
}),
methods: {
async login () {
let data;
// Submit the form.
try {
const response = await this.form.post('/api/login');
data = response.data;
} catch (e) {
return;
}
// Save the token.
this.$store.dispatch('auth/saveToken', {
token: data.token,
remember: this.remember
});
// Fetch the user.
await this.$store.dispatch('auth/fetchUser');
// Redirect home.
this.$router.push({ name: 'home' })
}
}
}
If I try to submit the login form with wrong email and password values I see an error message in a browser console.
For example:
POST http://laravel.local/api/login 422 (Unprocessable Entity)
Please note that I'm using try catch that catches all errors on the following call.
const response = await this.form.post('/api/login');
Is this really issue with async/await usage?
How can I get rid of that error in the browser console?
If you need some more info from me do not hesitate to ask it.
I've mostly utilised the Hapi framework to build RESTful APIs. For this project I'm using Express and I'm a bit lost as to why this is happening.
When I test the POST endpoint using Postman, the first request is fine, but I would get an error when I make the second request.
Error: Can't set headers after they are sent.
The code for the route handler is below:
const login = (req, res) => {
const validation = authScema.loginPayload.validate(req.body)
if (validation.error) {
return res.status(400).send(validation.error.details[0].message)
}
const { email, password } = req.body
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.catch(error => {
// Handle Errors here.
if (error) {
return res.status(400).send('Invalid login details.')
}
})
firebase.auth().onAuthStateChanged(user => {
if (user) {
const userObject = {
email: user.email,
uid: user.uid
}
const token = jwt.sign(userObject, secret)
return res.status(200).send(token)
}
})
}
I don't understand why headers are resent since in every branch, I return. It should have exited the function, right?
Turns out, signInWithEmailAndPassword
is a promise that returns the user in the happy path
So, the following is the final code:
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then(user => {
const userObject = {
email: user.email,
uid: user.uid
}
const token = jwt.sign(userObject, secret)
res.status(200).json({ token })
})
.catch(error => {
if (error) {
res.status(400).json({ message: 'Invalid login details.' })
}
})
The onOnAuthStateChanged is not necessary in this case.