nhandledPromiseRejectionWarning: ValidationError: user validation failed: password: Path `password` is required - validationerror

I'm trying to register users using node and mongo but I get this ValidationError:
unhandledPromiseRejectionWarning: ValidationError: user validation failed: password: Path password is required., username: Path username is required., email: Path email is required.
this is my signup function.
exports.signup = async function (request, res, next) {
try {
let user = await db.User.create(request.body);
console.log(user);
let { id,email,username } = user;
let token = jwt.sign({
id,
email,
username
},
process.env.SECRET_KEY
);
return res.status(200).json({
id,
username,
token
})
} catch (err) {
if (err.code === 11000) {
err.message = "sorry, username/email are token";
}
return next({
status: 400,
message: err.message
})
}
this is my user model
const userSchema = new mongoose.Schema({
email: {
type: String,
required: true,
unique: true,
},
username: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true,
},
profileImageUrl: {
type: String,
},
messages:[{
type:mongoose.Schema.Types.ObjectId,
ref:'Message'
}]
})
userSchema.pre('save', async function (next) {
try {
if (!this.isModified('password')) {
return next();
}
let hashedPassword = await bcrypt.hash(this.password, 10);
this.password = hashedPassword;
return next();
} catch (err) {
return next(err);
}
});
const User = mongoose.model("user", userSchema);
module.exports = User;
NOTE: I'm using Postman to test this.

So yeah I found the problem. the fields are required so if you try to insert a new user with empty fields you get that error. The fields were empty because The 'body-parser' middleware only handles JSON and urlencoded data, not multipart. So I had to change my index file to
app.use(bodyParser.urlencoded({
extended: true
}));
I also changed the content type in Postman to "X-www-form-urlencoded". Now the request body is populated and the user is inserted correctly

I also faced this error.
In your user model remove required:true in all fields like this:
email: {
type: String,
unique: true
}

Related

How to pass additional parameters in next-auth social login in NextJs?

I need to pass additional parameters to signIn function using next-auth in a NextJs project.
Here is what I tried.
<button
onClick={() =>
signIn(providers.facebook.id, { userType: "customer" })
}
>
<img src="images/facebook.svg" className="w-5 h-5" />
</button>
[...nextAuth].js code
import NextAuth from "next-auth";
import dbConnect from "../../../lib/dbConnect";
import CredentialsProvider from "next-auth/providers/credentials";
import User from "../../../models/User";
import brcypt from "bcryptjs";
import GoogleProvider from "next-auth/providers/google";
import FacebookProvider from "next-auth/providers/facebook";
import InstagramProvider from "next-auth/providers/instagram";
dbConnect();
export default NextAuth({
session: {
strategy: "jwt",
},
secret: process.env.NEXTAUTH_SECRET,
//The providers are the authentication method
providers: [
CredentialsProvider({
// The name to display on the sign in form (e.g. 'Sign in with...')
name: "Credentials",
// The credentials is used to generate a suitable form on the sign in page.
// You can specify whatever fields you are expecting to be submitted.
// e.g. domain, username, password, 2FA token, etc.
// You can pass any HTML attribute to the <input> tag through the object.
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" },
},
async authorize(credentials, req) {
try {
const email = credentials.email;
const password = credentials.password;
const user = await User.findOne({ email: email });
if (!user) {
return null;
}
if (user) {
let allow = await signInUser({ password, user });
if (allow == true) {
return user;
} else {
return null;
}
}
} catch (error) {
return null;
}
},
}),
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
authorizationUrl:
"https://accounts.google.com/o/oauth2/v2/auth?prompt=consent&access_type=offline&response_type=code",
}),
FacebookProvider({
clientId: process.env.FACEBOOK_CLIENT_ID,
clientSecret: process.env.FACEBOOK_CLIENT_SECRET,
authorization: {
params: {
userType: "customer" || "admin",
},
},
}),
InstagramProvider({
clientId: process.env.INSTAGRAM_CLIENT_ID,
clientSecret: process.env.INSTAGRAM_CLIENT_SECRET,
}),
],
pages: {
signIn: "/login",
},
database: process.env.MONGODB_URI,
callbacks: {
async jwt(token, profile) {
console.log("jwt token>>>>", token);
console.log("jwt profile>>>>", profile);
return token;
},
async session({ session, user, token }) {
if (token) {
const name = token.token.user.name;
const email = token.token.user.email;
const image = token.token.user.image;
const platform = token.token.account.provider;
handleUser(name, email, image, platform);
}
return token.token.token;
},
},
});
const signInUser = async ({ password, user }) => {
let allow = true;
if (!password) {
allow = false;
}
const isMatch = await brcypt.compare(password, user.password);
if (!isMatch) {
allow = false;
}
return allow;
};
async function handleUser(name, email, image, platform) {
console.log("Handle User>>>>>", name);
console.log("Handle email>>>>>", email);
console.log("Handle image>>>>>", image);
console.log("Handle platform>>>>>", platform);
}
Inside the callbacks function I tried logging token & profile. The additional params I passed is not being sent.
What is the right way to achieve this in Next.js?
You can find an article on the GitHub discussion section for next-auth here. The general gist of the answer is that you specify additional custom parameters in the Google Provider parameter (as below). There are other steps, but overall they appear to have solved it. Hope this helps.
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
authorization: {
params: {
prompt: "consent",
access_type: "offline",
response_type: "code",
userType: "user" || "admin", <-- THIS ONE
},
},
}),

Nodemailer: mail command failed

I keep getting the error:
Mail command failed: 554 5.7.8 User [contact#example.com] not authorized to send on behalf of <test#test.com>
This is my code:
api/contact.js
import nodemailer from "nodemailer"
export default async (req, res) => {
const { name, email, phone, message} = req.body;
const transporter = nodemailer.createTransport({
host: "send.one.com",
port: 465,
secure: false,
auth: {
user:'contact#example.com',
pass: 'password'
},
tls: {
rejectUnauthorized: false
}
});
try {
await transporter.sendMail({
from: {
name: req.body.name,
address: email
},
to: 'contact#example',
subject: `Contact form submission from ${name}`,
html: `<p>You have received a contact form submission</p><br>
<p><strong>Email: </strong> ${email}</p><br>
<p><strong>Phone: </strong> ${phone}</p><br>
<p><strong>Message: </strong> ${message}</p><br>`
});
} catch (error) {
return res.status(500).json({error: error.message || error.toString() })
}
return res.status(200).json({ error: ""});
};
contact.js:
import { useState } from 'react'
export default function Contact() {
const [inputs, setInputs] = useState({
name: '',
email: '',
phone: '',
message: ''
})
const [form, setForm] = useState('')
const handleChange = (e) => {
setInputs((prev) => ({
...prev,
[e.target.id]: e.target.value
}))
}
const onSubmitForm = async (e) => {
e.preventDefault()
if (inputs.name && inputs.email && inputs.phone && inputs.message) {
setForm({ state: 'loading' })
try {
const res = await fetch(`api/contact`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(inputs)
})
const { error } = await res.json()
if (error) {
setForm({
state: 'error',
message: error
})
return
}
setForm({
state: 'success',
message: 'Your message was sent successfully.'
})
setInputs({
name: '',
email: '',
phone: '',
message: ''
})
} catch (error) {
setForm({
state: 'error',
message: 'Something went wrong.'
})
}
}
}
None of my Google searches seem to bear any fruit. Does it have something to do with my domain provider? I have tested my code with Gmail, and it works like a charm, but not with one.com.
I am open for suggestions. This error has had me stumbled for days now.
The reason you're trying to send an email from an unauthorized source is because your from option is using data from the request. You wont be able to send an email from a source you aren't authorized to use. You should be sending from contact#example.com.
I'm not sure of the exact goal of the form, but consider redesigning the flow of the email service to send emails from your own source (contact#example.com) otherwise, you have to take the users email authorization credentials as input which can and will go south quickly.

How to use next-auth using ldap and prisma

i am using next-auth with ldap to authenticate user name and password. i am able to log/authenticate the user using username and password. but when i can't create user using prisma as await is not allowed inside promise.
this is my [...next-auth].js
`
const ldap = require("ldapjs");
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { PrismaClient } from "#prisma/client";
const url = `ldap://${process.env.LDAP_SERVER}`;
const prisma = new PrismaClient();
export default NextAuth({
providers: [
CredentialsProvider({
name: "LDAP",
credentials: {
username: { label: "DN", type: "text", placeholder: "" },
password: { label: "Password", type: "password" },
},
authorize: async (credentials, req) => {
// You might want to pull this call out so we're not making a new LDAP client on every login attemp
const client = ldap.createClient({
url: url,
});
return new Promise((resolve, reject) => {
client.bind(
`${credentials.username}#${process.env.LDAP_DOMAIN}`,
credentials.password,
(error) => {
if (error) {
console.log("Wrong email or password.");
reject("Wrong email or password.");
} else {
console.log("Successfully Logged In");
resolve({
username: credentials.username,
password: credentials.password,
});
}
const filter = `(sAMAccountName=${credentials.username})`;
client.search(
process.env.LDAP_BASE_DN,
{
filter,
scope: "sub",
attributes: [
"mail",
"employeeid",
"title",
"name",
"division",
"department",
"section",
],
},
(err, results) => {
if (err) {
reject(`User ${username} LDAP search error`);
}
const entries = [];
results.on("searchEntry", (entry) => {
entries.push(entry.object);
});
results.on("error", (err) => {
reject("LDAP SEARCH error");
});
results.on("end", (result) => {
if (entries.length == 0) {
reject("Something went wrong. Please try again. (AD)");
}
console.log({ entries });
const searchResult = JSON.stringify(entries[0]);
const adEmployee = JSON.parse(searchResult);
const empId = adEmployee?.employeeID;
const name = adEmployee.name;
console.log(empId);
const newUser= await prisma.user.findUnique({
where:{
oracleId:oracleId
}
})
if(!newUser){
await prisma.user.create({
data:{
oracleId:empId,
fullName:name
}
})
}
});
}
);
}
);
});
},
}),
],
pages: {
signIn: "/auth/sign-in",
},
callbacks: {
jwt: async ({ token, user }) => {
if (user) {
token.username = user.username;
token.password = user.password;
}
return token;
},
session: async ({ session, token }) => {
if (token) {
session.id = token.id;
session.username = token.username;
}
// console.log(token);
return session;
},
},
debug: process.env.NODE_ENV === "development",
secret: process.env.NEXTAUTH_SECRET,
jwt: {
secret: process.env.NEXTAUTH_SECRET,
encryption: true,
},
});
`
await is not allowed inside promise, where should i call prisma.
Thanks
For this u need to use API endpoint (as prisma is used on server side and cannot be used on client side especially when you pass db url from env also not shown on frontend), your create for example /api/register where:
import { PrismaClient } from '#prisma/client';
import dotenv from 'dotenv'
dotenv.config();
const prisma = new PrismaClient();
const Handler = async (
req,
res
) => {
await prisma.$connect()
const users = await prisma.user.findMany()
//check if user u add is already in db
//if not then
try {
savedUser = await prisma.user.create({ data: new_user });
await prisma.$disconnect()
} catch (error: any) {
await prisma.$disconnect()
// show db error
return res.status(501).json({message: error.message})
}
res.status(200).json({ message: 'User added to db ' + savedUser.name });
}
this is just a simple explanation of what you need to do to make it work, you may add some safety:
const { username, password } = req.body
if (req.method !== 'POST') {
return res.status(405).json({ message: 'Method not allowed' });
}
if (!username || !password) {
return res.status(400).json({ message: 'Username and password are required' });
}
then u call api endpoint:
const response = await axios.post(
LOGIN_URL,
JSON.stringify({ username, password }),
{
headers: { 'Content-Type': 'application/json' },
withCredentials: true
}
)
where LOGIN_URL could be /api/register

How to add additional data to signIn Promise return in NEXT-AUTH?

This is how we are authorizing users in our website
signIn('credentials', {
phoneNumber: verifiedPhone,
code: otp.data,
type: 'phone',
redirect: false,
}).then((res) => {
console.log(res) // default response {error,status,ok,url}
// How can i add additional data to this response, in my case user session
// if (!res.user.name) openModal('add_name')
// else toast.success('You are all set!')
});
By default, signIn will then return a Promise, that resolves:
{
error: string | undefined
status: number
ok: boolean
url: string | null
}
And we wanna add custom data to this promise return.
Actually what we wanna do is to sign user in and if the user is new, he/she is supposed to have no username so a modal opens up, enters his/her username and we update the next-auth session.
[...nextauth].js:
...
async authorize(credentials, req) {
// check the code here
const res = await requests.auth.signInEnterOtp(
credentials.phoneNumber,
credentials.code,
credentials.type
);
if (!res.ok) return null
return {
user: {
access_token: res.data?.access_token,
token_type: res.data?.token_type,
expires_at: res.data?.expires_at,
user_info: {
id: res.data?.user.id,
name: res.data?.user.name,
phone: res.data?.user.phone,
user_type: res.data?.user.user_type,
},
},
};
},
...
I eventually figured it like this:
...
.then(async (res) => {
const session = await getSession()
...
})
...
Bu i have another problem, it is to update the session with new username (
EDIT
i found a way of how to change session after sign in
[...nextauth].js :
...
async authorize(credentials, req){
...
if(credentials.type === 'update_name'){
const session = await getSession({ req })
return session.user.name = credentails.name
}
...
}
on the client :
signIn('credentials', {
name: newName,
type: 'name_update',
redirect: false
)

Nuxt middleware: How to access vuex store?

I am trying to block user on client-side from editing another user's profile. My URL structure is like so:
/users/edit/XpuBjKFoLSRHJAloNg38Amqn2jQ2
Thus, if user tries to acccess path of another user (ie, http://localhost:3000/users/edit/blahdasd) I need to redirect him to homepage.
I tried to set up an anonymous middle ware like so on my page:
export default {
middleware({ store, params, redirect }) {
if (store.state.user.currentUser.uid !== params.uid) {
return redirect('/')
}
},
But, I get page error of:
Cannot read property 'uid' of null
So, how do I correctly access the store here? I have no problem accessing uid from computed property on same page:
user() {
return this.$store.state.user.currentUser
},
Update (more information):
Here is my edit user profile page:
export default {
middleware({ store, params, redirect }) {
if (store.state.user.currentUser.uid !== params.uid) {
// return redirect('/')
console.log(store.state.user.currentUser.uid)
console.log(params.uid)
}
},
computed: {
user() {
return this.$store.state.user.currentUser
},
And here is my store/user.js file:
export const state = () => ({
currentUser: null,
})
export const mutations = {
SET_AUTH_USER(state, payload) {
state.currentUser = payload
}
}
export const actions = {
async onAuthStateChangedAction({ commit, dispatch }, { authUser }) {
console.log('auth state changed....')
try {
if (authUser && authUser.emailVerified) {
const {
uid,
email,
emailVerified,
displayName = '',
photoURL,
metadata,
providerData,
providerId,
tenantId
} = authUser
commit('SET_AUTH_USER', {
uid,
email,
emailVerified,
displayName,
photoURL,
metadata,
providerData,
providerId,
tenantId
})
console.log('fetching profile...')
await dispatch('getUserProfile', authUser)
} else {
console.log('User logged out or not verified')
return null
}
} catch (error) {
console.error('Error with Auth State observer: ', error)
}
},

Resources