I am building a Node.js project which has an advanced folder structure for future purposes.
user.register controller:
exports.register = async (req, res) => {
try {
var isValidated = await userService.validateInDatabase(req);
if (!isValidated)
return res
.status(409)
.json({ error: "Phone number or email is already registered" });
var user = await userService.create(req);
return user
} catch (e) {
console.trace(e);
return res.status(400).json({ message: e.message });
}
};
The services file code:
exports.create = async (user) => {
const hashedPassword = passwordHash.generate(user.password);
let new_user = new User({
phoneNumber,
email,
password: hashedPassword,
});
const payload = {
id: new_user._id,
};
let token = jwt.sign(payload, keys.JWToken, { expiresIn: 31556926 });
const userData = await new_user.save();
return userData;
};
exports.validateInDatabase = async (req) => {
let check_user = await User.findOne({
$or: [{ email: req.body.email }, { phoneNumber: req.body.phoneNumber }],
});
if (check_user) return false;
return true;
};
Now, whenever I send the request from the postman it says invalid password Why is that?
Related
I was trying to separate the logic of my back-end server into route/controller/service,
but one problem is when I try to catch error in service layer, I will return error, but how can I determine if it's error and return 404 status in my controller layer ?
Here's the code
Service layer
const register = async(name, email, password) => {
const newUser = new User({
username: name,
email: email,
password: CryptoJS.AES.encrypt(password, key).toString(),
});
try{
const savedUser = await newUser.save();
return savedUser
} catch (err){
return err
}
};
Controller layer
const register = (req,res) => {
const { name, email, password } = req.body
const user = UserService.register(name,email,password);
if(how to determine here?){
res.status(201).json(user);
}
}
You could return an object in your Service Layer instead of an User or Error object:
const register = async(name, email, password) => {
const newUser = new User({
username: name,
email: email,
password: CryptoJS.AES.encrypt(password, key).toString(),
});
try{
const savedUser = await newUser.save();
return { user: savedUser, error: null }
} catch (error){
return { error, user: null }
}
};
In your Controller Layer you would then do something like this:
const register = (req,res) => {
const { name, email, password } = req.body
const { user, error } = UserService.register(name,email,password);
if(error){
res.status(500).json({ user, error: error.message });
} else {
res.status(200).json({ user, error })
}
}
return the save promise and handle errors in your controller
serivce:
const register = async(name, email, password) => {
const newUser = new User({
username: name,
email: email,
password: CryptoJS.AES.encrypt(password, key).toString(),
});
return newUser.save();
};
controller:
const register = async (req,res) => {
const { name, email, password } = req.body
try {
const user = await UserService.register(name,email,password);
res.status(201).json(user);
} catch(error) {
// handle error here
}
}
I have a project with the below Model, Controller, and Route File for user. I am wanting to implement sequelize, which I have managed to partially achieve using the account files below, however, I am struggling to work out how to implement logging in and ensuring a request has a valid token usijng user.ensureToken which would become account.ensureToken.
I'm fairly new to Node.Js so I'm not even sure where to start
user.model.js
const bcrypt = require('bcrypt');
const sql = require("./db.js");
const HashBits = require("../config/auth.config.js");
const faker = require('faker');
// constructor
const User = function(user) {
this.first_name = user.first_name;
this.last_name = user.last_name;
this.mob_no = user.mob_no;
this.user_name = user.user_name;
this.password = user.password;
};
User.create = (newUser, result) => {
bcrypt.hash(newUser.password, HashBits.saltRounds, (err, hash) => {
newUser.password = hash;
sql.query("INSERT INTO users SET ?", newUser, (err, res) => {
if (err) {
// console.log("error: ", err);
result(err, null);
return;
}
newID = res.insertId;
// console.log("created user: ", { id: res.insertId, ...newUser });
result(null, { id: res.insertId, ...newUser });
});
});
};
User.authenticate = (user,result) => {
// sql.query(`SELECT * FROM customers WHERE id = ${customerId}`, (err, res) => {
sql.query(`SELECT * FROM users WHERE user_name = '${user.user_name}'`, (err, res) => {
// sql.query("SELECT * FROM users ", (err, res) => {
if (err) {
// console.log("error: ", err);
result(err, null);
return;
}
if(res.length !== 1){
// console.log("error: found multiple users");
result("error: found multiple users", null);
return;
}
// console.log("Found user: ",res[0]);
bcrypt.compare(user.password, res[0].password, function(err, res2) {
if(res2){
// console.log("Yes");
result(null,res[0]);
}else{
// console.log("On ya bike");
result("ERROR",null);
// return;
}
});
});
};
module.exports = User;
user.controller.js
const User = require("../models/user.model.js");
var jwt = require("jsonwebtoken");
const config = require("../config/auth.config.js");
// Create and Save a new User
exports.create = (req, res) => {
// Validate request
if (!req.body) {
res.status(400).send({
message: "Content can not be empty!"
});
}
// Create a User
const user = new User({
first_name: req.body.first_name,
last_name: req.body.last_name,
mob_no: req.body.mob_no,
user_name: req.body.user_name,
password: req.body.password
});
// Save User in the database
User.create(user, (err, data) => {
if (err)
res.status(500).send({
message:
err.message || "Some error occurred while creating the User."
});
else res.send(data);
});
};
exports.authenticate = (req,res) => {
if (!req.body) {
res.status(400).send({
message: "Content can not be empty!"
});
}
const user = new User({
user_name: req.body.user_name,
password: req.body.password
});
User.authenticate(user, (err,data) => {
if(err)
res.status(500).send({
message:
err.message || "Some error occurred while authenticating the User."
});
else {
var token = jwt.sign({ id: user.id }, config.secret, {
expiresIn: 86400 // 24 hours
});
// res.send(data);
res.status(200).send({
id: data.id,
username: data.user_name,
accessToken: token
});
}
});
};
exports.ensureToken = (req, res, next) => {
let token = req.headers["x-access-token"];
if (!token) {
return res.status(403).send({
message: "No token provided!"
});
}
jwt.verify(token, config.secret, (err, decoded) => {
if (err) {
return res.status(401).send({
message: "Unauthorized!"
});
}
req.userId = decoded.id;
next();
});
}
user.routes.js
module.exports = app => {
const users = require("../controllers/user.controller.js");
// Create a new User
app.post("/User", users.create);
// Login
app.post("/User/Login", users.authenticate);
};
account.model.js
const bcrypt = require("bcrypt");
module.exports = (sequelize, Sequelize) => {
const Account = sequelize.define("account", {
firstName: {
type: Sequelize.STRING
},
username: {
type: Sequelize.STRING
},
password: {
type: Sequelize.STRING,
set(value){
const hash = bcrypt.hashSync(value, 10);
this.setDataValue('password', hash);
}
}
});
return Account;
};
account.controller.js
const db = require("../models");
const Account = db.accounts;
const Op = db.Sequelize.Op;
var jwt = require("jsonwebtoken");
const config = require("../config/auth.config.js");
// Create and Save a new New
exports.create = (req, res) => {
// Validate request
if (!req.body.username) {
res.status(400).send({
message: "Content can not be empty!"
});
return;
}
// Create a Site
const account = {
firstName: req.body.firstName,
username: req.body.username,
password: req.body.password
};
Account.create(account)
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while creating the Account."
});
});
};
exports.authenticate = (req,res) => {
if (!req.body) {
res.status(400).send({
message: "Content can not be empty!"
});
}
const account = new Account({
username: req.body.username,
password: req.body.password
});
};
account.routes.js
module.exports = app => {
const accounts = require("../controllers/account.controller.js");
var router = require("express").Router();
app.post("/account", accounts.create);
app.post("/account/Login", accounts.authenticate);
};
you need to use jwt token for access token as you said and you are bcrypt password in model file which will be security issue you have to bcrypt password as soon as it comes in request I have implemented it in my answer remove code of password bcrypt from your model file
you have to import in your account.controller.js
const db = require("../models");
const User = db.user;
require('dotenv').config();
const Op = db.Sequelize.Op;
const errors = require('../config/errors');
const error = errors.errors;
var jwt = require("jsonwebtoken");
var bcrypt = require("bcryptjs");
module.exports = {
signup: async (req, res) => {
if (!req.body.first_name|| !req.body.lastt_name || !req.body.password) {
return res.status(200).send(error.MANDATORY_FIELDS);
}
try {
// Save User to Database
User.create({
name: req.body.name,
email: req.body.email,
mo_no: req.body.mo_no,
city: req.body.city,
password: bcrypt.hashSync(req.body.password, 8),
user_type: "admin"
}).then(function (user) {
return res.status(200).send(error.OK)
})
.catch(function (err) {
console.log(err);
return res.status(500).send(error.SERVER_ERROR)
});
} catch (e) {
console.log(e);
return res.status(500).send(error.SERVER_ERROR)
}
},
signin: async (req, res) => {
if (!req.body.email || !req.body.password) {
return res.status(200).send(error.MANDATORY_FIELDS);
}
User.findOne({
where: {
email: req.body.email
}
}).then(function (user) {
if (!user) {
return res.status(404).send(error.USER_NOT_PRESENT);
}
const passwordIsValid = bcrypt.compareSync(
req.body.password,
user.password
);
if (!passwordIsValid) {
return res.status(422).send(error.PASSWORD_MISSMATCH, {
accessToken: null
});
}
const token = jwt.sign({ id: user.id, first_name: user.first_name },
process.env.secret, {
expiresIn: 86400 // 24 hours
});
const authorities = [];
return res.status(200).send({
id: user.id,
name: user.name,
email: user.email,
accessToken: token
});
});
})
.catch(function (err) {
console.log(err)
return res.status(500).send(error.SERVER_ERROR);
});
}
}
than you have to create a separate folder for authorization like authorize.js or authJwt.js where you have to check is token is valid or not put this code in authorize.js
at decoding time secret token or password also needed which you have in .env file
const jwt = require("jsonwebtoken");
verifyToken = (req, res, next) => {
let token = req.headers["x-access-token"];
if (!token) {
return res.status(403).send(error.TOKEN_NOT_PROVIDED);
}
jwt.verify(token, process.env.secret, (err, decoded) => {
if (err) {
return res.status(401).send(error.UNAUTHORIZED);
}
req.first_name= decoded.first_name;
req.id = decoded.user_id
next();
});
};
const authJwt = {
verifyToken: verifyToken
};
module.exports = authJwt;
than you have to import authorize.js file in your routes whenever you want like this
const authorize = require('../authorize.js');
module.exports = app => {
const accounts = require("../controllers/account.controller.js");
var router = require("express").Router();
app.post("/account", accounts.create);
app.post("/account/Login",
authorize.verifyToken,accounts.authenticate);
};
it will be more effective if you genreate access token at signin time
I have a user register function which is using async/await and I need to implement some code from another API. When I try to integrate it I get an error that I can't use await outside an async function.
exports.register = async (req, res) => {
// some logic here
nexmo.verify.request(
{
number: formattedMobile,
brand: "My Brand",
code_length: "4",
},
(err, result) => {
if (err) {
// If there was an error, return it to the client
return res.status(500).send(err.error_text);
}
// Otherwise, send back the request id. This data is integral to the next step
const requestId = result.request_id;
const salt = await bcrypt.genSalt(12);
const hashedPassword = await bcrypt.hash(password, salt);
const createdUser = new User({
name: name,
email: email,
mobile: formattedMobile,
password: hashedPassword,
});
try {
await createdUser.save();
res.status(200).send({ user: createdUser._id, otp: requestId });
} catch (err) {
res.status(500).send(err);
}
}
You need to make the callback function async, and most likely wrap the entire code in a try catch block to handle errors.
async (err, result) => {
if (err) {
// If there was an error, return it to the client
return res.status(500).send(err.error_text);
}
try {
// Otherwise, send back the request id. This data is integral to the next step
const requestId = result.request_id;
const salt = await bcrypt.genSalt(12);
const hashedPassword = await bcrypt.hash(password, salt);
const createdUser = new User({
name: name,
email: email,
mobile: formattedMobile,
password: hashedPassword,
});
try {
await createdUser.save();
res.status(200).send({ user: createdUser._id, otp: requestId });
} catch (err) {
res.status(500).send(err);
}
} catch(err) {
console.log(err);//do whatever error handling here
}
}
I'm trying to get my login api working inside azure function, but it keeps saying await is only valid in async function. This is an async function so I'm just super confused.
This line
const user = await db.collection('users').findOne({ email: userLoggingIn.email })
is throwing the error.
const jwt = require("jsonwebtoken");
const bcrypt = require("bcrypt");
var MongoClient = require('mongodb').MongoClient;
module.exports = async function (context, req) {
MongoClient.connect(process.env.CosmosDBConnectionString, (err, client) => {
let userLoggingIn = ({ email, password } = req.body);
console.log("userLoggingIn");
console.log(userLoggingIn.email);
let send = response(client, context);
if (err) send(500, err.message);
console.log("DBNAME: " + process.env.dbName);
let user;
let db = client.db(process.env.dbName);
const user = await db.collection('users').findOne({ email: userLoggingIn.email })
console.log("USER");
console.log(user);
let userName= user.instagramName;
if (!user) {
send(401, { message: "Auth failed" });
}
const { username } = user;
console.log("PASSWORD");
console.log(context.req.password);
console.log(user.password);
const goodPassword = bcrypt.compareSync(context.req.password, user.password);
if (!goodPassword) {
return send(401, { message: "Auth failed" });
}
const token = jwt.sign(
{
email: user.email,
userId: user._id,
username: userName
},
"secret_this_should_be_longer",
{ expiresIn: "1h" }
);
context.res = { status: 200, token: token, expiresIn: 3600, userId: user._id, username: username};
})
}
function response(client, context) {
return function (status, body) {
context.res = {
status: status,
body: body
};
client.close();
context.done();
};
}
MongoClient.connect(process.env.CosmosDBConnectionString, (err, client) => {
on this line, the annonymous callback function receiving err and client as parameter is the function that needs to be async
MongoClient.connect(process.env.CosmosDBConnectionString, async (err, client) => {
I'm trying to run a test using Jest for this Nodejs API. Although everything works when I run the app, it's failing and returning the error
TypeError: Cannot read property 'store' of undefined
The test code:
const request = require('supertest')
const server = require('../../server')
const { User } = require('../../app/models/User')
describe('User', () => {
test('should create user', async () => {
const user = await User.store({
name: 'Marcelo',
email: 'marcelo#vuttr.com',
password: '123456'
})
const response = await request(server)
.post('/user')
.send({
name: user.name,
email: user.email,
password: user.password
})
expect(response.status).toBe(200)
})
})
The controller:
const User = require('../models/User')
class UserController {
async store (req, res) {
const { email } = req.body
if (await User.findOne({ email })) {
return res.status(400).json({ error: 'User already exists' })
}
const user = await User.create(req.body)
return res.json(user)
}
}
module.exports = new UserController()
For more details, I share this project in github.
try this code
the Test Code
const request = require('supertest')
const server = require('../../server')
const { User } = require('../../app/models/User')
const { UserController } = require('../../UserController')
describe('User', () => {
test('should create user', async () => {
const user = await UserController({
name: 'Marcelo',
email: 'marcelo#vuttr.com',
password: '123456'
})
const response = await request(server)
.post('/user')
.send({
name: user.name,
email: user.email,
password: user.password
})
expect(response.status).toBe(200)
})
})
The controller:
const User = require('../models/User')
const UserController = async(req, res) => {
const { email } = req.body
if (await User.findOne({ email })) {
return res.status(400).json({ error: 'User already exists' })
}
const user = await User.create(req.body);
return res.status(201).json(user);
}
module.exports = UserController;