option to manually flush/write body to browser when done - mongodb

I have so far been unable to sort out this issue. I have a code that uses the MongoDb driver and whenever a do a fetch operation, i can no longer write to .response.body, e.g:
Once I call - await users.findOne({ email: req.email }) , I get an error when I do ctx.response.body.
As a workaround, is there a way i can force write the response, or a flag i can use to force oak not to close the response until i explicitly tell it to?
The error I get: The response is not writable.
Here is a sample snipped of my code:
private async create(context: Context, next: Function){
try {
interface UserSchema { email: string; password: string;}
const body = context.request.body()
assertEquals(body.type, 'json', 'app sent data in wrong format')
const req = await body.value
assertExists(req.email, 'email not provided')
assertExists(req.password, 'password not provided')
assertMatch(req.email, /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")#(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/, 'this does not look like a valid email address')
assertMatch(req.password, /(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.{8,})/, 'password must contain: capital letter, small letter, number and must be 8 characters long')
const conn = context.state.db
const db = await conn.database('users')
const users = await db.collection<UserSchema>("users")
const user_id = await users.findOne({ email: req.email }) //bug *wont write to body
assert(!(user_id), 'email already exists')
const insertId = await users.insertOne({ email: req.email, password: req.password })
console.log('user added'); context.response.body = { 'error': false, 'msg': 'account created' }
} catch (e) { console.log({ 'error': e.name, 'msg': e.message })
//context.response.status = 500; context.response.body = { 'error': e.name, 'msg': e.message }
}
}

Related

Login post with Bcrypt always return false

Bcrypt.compare returns false no matter what
I'm currently working on a login/register feature in NextJS that utilizes bcrypt to hash and compare user passwords. I'm able to register a user with a hashed password, but when attempting to log in with bcrypt.compare(), the comparison always returns false, even when the entered password matches the hashed password.
The issue lies in this line: const isPasswordMatched = await bcrypt.compare(password, user.password);, where the compare() method is used to compare the entered password with the hashed password. Despite the method's implementation, it's not working as expected.
api/auth/[...nextauth].ts for login
const authOptions: NextAuthOptions = {
session: {
strategy: "jwt",
},
providers: [
CredentialsProvider({
async authorize(credentials, req) {
await connectDB();
const { email, password }: Icredential = credentials;
// Find user by email
const user = await User.findOne({ email: email });
if (user === null) {
throw new Error('Cannot find user');
}
// Check if password from input matches with the one from db
// This is the line of the concern
const isPasswordMatched = await bcrypt.compare(password, user.password);
console.log(`Comparing ${password} to ${user.password}`);
console.log("match ?", isPasswordMatched);
// Throw error when it doesn't
if (!isPasswordMatched)
// if (password !== '123')
{
throw new Error('Invalid email or password');
}
// Return authorized user
return user;
},
credentials: undefined
}),
],
};
export default NextAuth(authOptions);
api/register for register
const registerHandler = async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method === "POST") {
try {
const { user: _regiUser } = req.body;
console.log(_regiUser)
//Check if user exists
await connectDB()
const existingUser = await User.findOne({ email: _regiUser.email }).exec();
console.log("existingUser", existingUser);
//Throw error when email is already in use
if (existingUser) {
throw new Error("Email already used");
}
//Password encrypted
const hashedPassword: string = await bcrypt.hashSync( _regiUser.password, 10 );
console.log("_regiUser.password", _regiUser.password, hashedPassword)
console.log(hashedPassword)
//Replace text password with encrypted password
_regiUser.password = hashedPassword;
console.log(_regiUser)
//Add user on database
await User.create(_regiUser)
res.end()
} catch (e: any) {
console.log(e.message)
}
}
};
export default registerHandler;
Login logic was completely correct, but I had
wrong User model like following:
const userSchema = new mongoose.Schema({
email: {
type: String,
required: true,
lowercase: true
},
password: {
type: String,
required: true,
lowercase: true //remove this to make it work
}
});
look at password entity, because I copy pasted from email entity, i had a wrong configuration for password. So hash stored in lowercase and this is the very reason why i got error no matter what. smh...
You're creating your password hashes using the hashSync() method (not async) but trying to run the async .compare() method when logging-in. Check out the examples.
For the comparison, you should be using:
bcrypt.compareSync(myPlaintextPassword, hash);
Otherwise, I recommend using the async/await bcrypt.hash and bcrypt.compare methods. If you want to use await bcrypto.compare(...), create your hash using:
await bcrypt.hash(password, 10);

How to solve client fetch error for next-auth authentication

I have created an app that connects to a mongodb cluster and stores user info. The user is then able to log in with Next-Auth functionality. The app was working just fine before deploying to Vercel. On the live site I ran into some Server Config Errors. I refractored my code yet I am still running into a few errors.
I am successfully able to connect to the database for a new user sign up.
import {
connectToDatabase,
hashedPassword,
} from "../../helper/HelperFunctions";
const isEmpty = (value) => value.trim() === "";
const isTenChars = (value) => value.trim().length >= 10;
const emailValidation = (value) => {
const pattern = /^[^ ]+#[^ ]+\.[a-z]{2,3}$/;
if (value.match(pattern)) {
return true;
} else {
return false;
}
};
export default async function handler(req, res) {
if (req.method == "POST") {
let data = req.body;
const { firstName, lastName, email, password, userName } = data;
const firstNameIsValid = !isEmpty(firstName);
const lastNameisValid = !isEmpty(lastName);
const emailIsValid = emailValidation(email);
const passwordisValid = isTenChars(password);
const userNameIsValid = !isEmpty(userName);
let userDataIsValid =
firstNameIsValid &&
lastNameisValid &&
emailIsValid &&
passwordisValid &&
userNameIsValid;
if (!userDataIsValid) {
return;
}
const client = await connectToDatabase();
const db = client.db();
const existingUser = await db.collection("users").findOne({ email: email });
if (existingUser) {
res.status(422).json({ message: "User already exists, please log in!" });
console.log("User already exists, please log in!");
client.close();
return;
}
const protectedPassword = await hashedPassword(password);
await db.collection("users").insertOne({
firstName: firstName,
lastName: lastName,
email: email,
password: protectedPassword,
userName: userName,
});
client.close();
res.status(201).json({ message: "Signed up!" });
} else {
res.status(200).json({ data: req.body });
}
}
Here is my nextauth api route
import NextAuth from "next-auth/next";
import CredentialsProvider from "next-auth/providers/credentials";
// Helper Functions
import {
connectToDatabase,
comparePasswords,
} from "../../../helper/HelperFunctions";
export default NextAuth({
session: { strategy: "jwt" },
providers: [
CredentialsProvider({
async authorize(credentials) {
const client = await connectToDatabase();
const userCollection = client.db().collection("users");
const user = await userCollection.findOne({
email: credentials.email,
});
if (!user) {
client.close();
throw new Error("No user found!");
}
const isValid = await comparePasswords(
credentials.password,
user.password
);
if (!isValid) {
client.close();
throw new Error("Invalid password");
}
client.close();
if (user) {
return {
email: user.email,
};
} else {
return null;
}
},
}),
],
});
Before I deployed my site on Vercel, this was working just fine on localhost. The user should then proceed to a new page if the result of logging in has no errors.
const result = await signIn("credentials", {
redirect: false,
email: form.email,
password: form.password,
});
if (!result.error) {
console.log(true);
router.replace("/suggestions");
} else {
console.log(result.error);
setLoginResult(result.error);
}
If you see CLIENT_FETCH_ERROR make sure you have configured the NEXTAUTH_URL environment variable.
when developing you set it to localhost:3000, now you need to set that to your deployed url.

findByIdAndUpdate not updating document on MongoDB

I am trying to create an Api that updates my MongoDB before sending a password reset email to the user using nodemailer. Everything works fine except the database update for some reason. I am using findByIdAndUpdate to do the update.
My api starts with
router.put('/forgot',[auth, [check('email', 'Please include a valid email').isEmail()]],async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { email } = req.body;
try {
let user = await User.findOne({ email });
if (!user) {
return res.status(400).json({
errors: [
{
msg:
'That email addrss is not recognized. Please try again or register for a new account ',
},
],
});
}
var email_token = crypto.randomBytes(64).toString('hex');
const payload = {
id: user.id,
resetPasswordToken: email_token,
resetPasswordExpires: Date.now() + 3600000,
};
user = await User.findByIdAndUpdate(
user.id,
{ $set: payload },
{ new: true }
);
console.log(user);
res.json(user);
Thank you Joe and Mohammed, Well from Mohammed question i realized i did not define resetPasswordToken and resetPasswordExpires in the User Model. As soon as i did that every thing worked as magic. Thank you so much!

Fail to save user to mongodb

If I remain this code, the program still working, my image will upload backend to frontend normally
router.post('/admin/register', upload.single('avatar'), async (req, res) => {
// Handle add image by multer
... handel file upload from front-end
return res.json({ avatar: newFullPath });
}
);
I started save user to mongoDB and error occur
router.post('/admin/register', upload.single('avatar'), async (req, res) => {
// Handle add image by multer
... handel file upload from front-end
//Handle add user to database
const user = {
...JSON.parse(req.body.user),
avatar: newFullPath
}; // { first_name: 'John', last_name: 'Wick', avatar: .... }
const { error } = Validation.adminRegisterValidation(user);
if (error) {
return res.json({ error: error.details[0].message });
} // working as I expected
const emailExist = await User.findOne({ email: user.email });
if (emailExist) {
return res.json({ error: 'Email already exist!' });
} // working as I expected
// If I commented this block of code, program still run as I expected, but if I don't do
// that, the program crashed ( Error: Below images )
const hashedPassword = bcrypt.hashSync(user.password, 10);
const addUser = new User({
first_name: user.first_name,
last_name: user.last_name,
avatar: user.avatar
});
await addUser.save();
return res.json({ avatar: newFullPath });
}
);
This project in my Github repository: This project in Github
Error shows in console
Error in Network

Express and mongodb (intermediate value) is not a constructor

I am trying to register a new user in my db.
I am using redux as well for this.
I am confused on what the error message is indicating is the problem.
I am confronted with the 500 server error and this line to follow?:
'(intermediate value) is not a constructor'
To give you a deeper understanding as to where this might be occurring, I have pasted some important parts of my code that initiates this sequence of events that lead to this.
Here's my action:
accountApi.register(
email,
password,
firstName,
lastName,
(errObj, resObj) => {
if (errObj) {
return dispatch({
type: REGISTER_USER_FAILED,
payload: new Error(errObj.detail)
});
}
const { token } = resObj;
storage.save(JWT_TOKEN, token, err => {
if (err) {
return dispatch({
type: REGISTER_USER_FAILED,
payload: err
});
}
return dispatch({
type: REGISTER_USER_SUCCESS,
payload: true
});
});
}
);
Here's the api route:
const body = {
email,
password,
firstName,
lastName,
date: today,
};
const ROUTE = "register";
const url = BASE_ROUTE + ROUTE;
client.post({ url, body }, cb);
}
inside API:
async function register(req, res) {
req.checkBody('email').notEmpty().isEmail();
req.checkBody('password')
.notEmpty()
.isAscii()
.isLength({ min: 8 });
req.checkBody('firstName')
.notEmpty()
.isAscii()
.isLength({ min: 3, max: 15 });
req.checkBody('lastName')
.notEmpty()
.isAscii()
.isLength({ min: 3, max: 15 });
});
const validationErrors = await req.getValidationResult();
if (!validationErrors.isEmpty()) {
return res.status(400).send(responses.validatorResponse(validationErrors));
}
const { email, password, firstName, lastName } = req.body;
const userDoc = await accountManager.register(email, password, firstName,
lastName, false);
return res.status(200).send({ token: jwtHelper.createToken(userDoc) });
}
in accountmanger:
async function register(email, password, firstName, lastName, isAdmin) {
const associationDoc = await Association.getDocumentByEmail(email);
if (!associationDoc) {
throw new new Error('Unable to register user. User is not associated with
any organization yet.')();
}
const teamDoc = await
Team.getDefaultTeamForOrganization(associationDoc.organizationId);
const hashedPassword = account.hashPassword(password);
const data = {
firstName: firstName.toLowerCase(),
lastName: lastName.toLowerCase(),
email,
isAdmin,
fullName: `${firstName.toLowerCase()} ${lastName.toLowerCase()}`,
teamId: teamDoc._id,
hashedPassword,
registrationStatus: REGISTRATION_STATUSES.NEW_REGISTRATION,
lastSeen: dates.today(),
};
return User.create(data);
}
And inside user model:
async function create(data) {
return new Model(data).save();
}
I use postman to send the api calls with all the proper info but I keep getting this error message back:
(intermediate value) is not a constructor