Error while log in authentication in Node js - javascript

I have created an API for LogIn authentication. I have used Sequelize ORM for Mysql Database. Given below is an image of my users model which i have imported in my authentication code for login.
Models/users image
module.exports = (sequelize, Sequelize) => {
const Tutorial = sequelize.define("users", {
age: { type: Sequelize.INTEGER },
name: { type: Sequelize.STRING },
email: { type: Sequelize.STRING },
password: { type: Sequelize.STRING }
});
return Tutorial
};
Here below is the code for log in authentication. I have used findOne function for getting the email & password and i have used .then which is a promise function that returns the response.
var users = require('./models/users');
app.post('/login', (req, res) => {
var email = req.body.email;
var password = req.body.password;
users.findOne({ email: email, password: password })
.then(users => {
if (users == null) {
res.status(404).json({
message: 'Auth Failed'
})
}
else {
res.status(200).json({
message: 'Logg In succesfull'
})
}
})
})
But when i hit the API, it shows error
TypeError: users.findOne is not a function
at D:\Node\Task\server.js:39:11
Please help me fix it.

module.exports = (sequelize, Sequelize) => {
Because your function accepts 2 parameters, you'll have to pass the values as well in the require to get it working.
const sequel = require('sequelize');
const DataTypes = sequel.DataTypes;
const sequelizeInstance = new sequel(...) // import your sequelize config
var users = require('./models/users')(sequelizeInstance, DataTypes);

I know nothing about Sequelize, but inspired by #sid's answer, I guess something like this is cleaner than importing Sequelize in the main file, then pass it to the imported file... Just import your dependencies in each file.
models/users :
const sequel = require('sequelize');
const DataTypes = sequel.DataTypes;
const sequelizeInstance = new sequel(...) // import your sequelize config
module.exports = sequelize.define("users", {
age: { type: DataTypes.INTEGER },
name: { type: DataTypes.STRING },
email: { type: DataTypes.STRING },
password: { type: DataTypes.STRING }
})
Then simply import it :
const users = require('./models/users');
users.findOne({ email, password })

If this is still unanswered it could be because of two reasons. Either you're importing the model wrong, or its because you're calling the query without it being async (As I've had similar issues with this as a cause):
const { users } = require('./models')
async app.post('/login', (req, res) => {
try {
const email = req.body.email
const password = req.body.password
const user = await users.findOne({
where: {
email: email,
password: password // Remove this if you have a verify password func
}
})
if (!user) {
res.status(403).send({
error: "Password or email is wrong"
})
}
// You should really encrypt the passwords and have a compare/verify function instead
// Example
if (!bcrypy.compareSync(password, user.password)) {
res.status(403).send({
error: "Password or email is wrong"
})
}
res.staus(200).send(user) // Save user to store/cache on client
} catch (err) {
console.log(err)
res.status(500).send({
error: "Error occured during login"
})
}
})
Once you have more of your frontend setup, you should send the user back to the client and then store it in client side cache or smth, so you don't need to query the server for logged in user data everytime.

Related

Error while importing a function "generateToken" to a middleware in Node js

I have created a model and the name of the table is users. In the Model, i have a method generateToken which is used to generate the web token.
I have used sequelized ORM.
module.exports = (sequelize, Sequelize) => {
const Tutorial = sequelize.define("users", {
age: {
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
email: {
type: Sequelize.STRING
},
password: {
type: Sequelize.STRING
}
});
Tutorial.generateToken = async function () {
try {
const token = jwt.sign({ _id: this.id }, "ThisIsTaskApp")
console.log(token)
}
catch (error) {
response.send('there is an error' + error)
console.log('there is an error' + error)
}
}
return Tutorial;
};
I want to create a web token when my email id and password matches, so for that i have used the generateToken but i am getting an error
TypeError: user.generateToken is not a function
I believe i have error with javascript importing the generateToken function.
const jwt = require('jsonwebtoken')
const user = db.users;
const generateToken = require('./models/users')
app.post('/login', async (req, res) => {
try {
var email = req.body.email;
var password = req.body.password;
await user.findOne({ where: { email: email, password: password } })
.then(user => {
if (user === null) {
res.status(404).json({
message: 'Auth Failed'
})
}
else {
const token = user.generateToken()
res.status(200).json(user)
}
})
}
catch (error) {
console.log(error)
return res.json({ 'status': 400 })
}
})
Please help me fix this issue and generating web token.
Try using
generateToken.generateToken()
there instead of
user.generateToken()
Because you are basically exporting the model of users in generate token variable, so that function is accessible from that variable not from user variable.
There is some issue with your code related to async, please try this one
const user = db.users;
app.post("/login", async (req, res) => {
try {
var email = req.body.email;
var password = req.body.password;
const userdata = await user.findOne({ where: { email: email, password: password } });
if (userdata === null) {
return res.status(404).json({
message: "Auth Failed",
});
}
const token = await userdata.generateToken();
console.log("🚀 ~ token", token)
return res.status(200).json(userdata);
} catch (error) {
console.log(error);
return res.json({ status: 400 });
}
});
I think you need to require jsonwebtoken in /models/users as well as in the route handler file

User is added to mongoDB database after registration despite "username" chosen already existing

I am developping a web app with the MERN stack.
On Postman or on my front-end form, when I register a user with an existing email, the user is not added to database. I use the same logic to check if the username picked by the user is already taken. If it's taken, the error message is dispatched but the user is still added to the database.
In the User model, the field Username is unique, just like the field Email.
My register route:
const express = require("express");
const router = express.Router();
const User = require("../../models/User");
const { check, validationResult } = require("express-validator");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const config = require("config");
// #route GET register
// #desc Register
// #access Public
router.get("/", (req, res) => {
res.send("Create an account");
});
// #route POST register
// #desc Register
// #access Public
router.post(
"/",
[
check("email", "Please, include a valid email.").isEmail(),
check(
"password",
"Please, enter a password with 6 or more characters"
).isLength({ min: 6 }),
check("username", "Please, include a valid username.")
.not()
.isEmpty()
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const {
email,
password,
birthdate,
birthplace,
sun,
username,
date,
is_paying
} = req.body;
try {
// See if user exists
let user = await User.findOne({ $or: [{ username }, { email }] });
if (user) {
res.status(400).json({ errors: [{ msg: "User already exists" }] });
}
// Create new user from User Model
user = new User({
email,
password,
birthdate,
birthplace,
sun,
username,
date,
is_paying
});
// Encrypt password
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(password, salt);
// Add user to database
await user.save();
// Return JWT
const payload = {
user: {
id: user.id
}
};
jwt.sign(
payload,
config.get("jwtSecret"),
{ expiresIn: 360000 },
(err, token) => {
if (err) throw err;
res.json({ token });
}
);
} catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
}
);
module.exports = router;
My register action :
// REGISTER USER
export const register = ({
email,
password,
birthdate,
gender,
sun,
username
}) => async dispatch => {
const config = {
headers: {
"Content-Type": "application/json"
}
};
const body = JSON.stringify({
email,
password,
birthdate,
gender,
sun,
username
});
try {
const res = await axios.post("/register", body, config);
dispatch({
type: REGISTER_SUCCESS,
payload: res.data
});
dispatch(loadUser());
} catch (err) {
const errors = err.response.data.errors;
if (errors) {
errors.forEach(error => dispatch(setAlert(error.msg, "danger")));
}
dispatch({
type: REGISTER_FAIL
});
}
};
You need to stop processing the request if the user exists:
// See if user exists
let user = await User.findOne({ $or: [{ username }, { email }] });
if (user) {
res.status(400).json({ errors: [{ msg: "User already exists" }] });
}
// Create new user from User Model
user = new User({
email,
password,
birthdate,
birthplace,
sun,
username,
date,
is_paying
});
See that? if (user) { res.json() } but you still go on. Make that return res.json() for a quick fix.
A bit better fix is you should set a unique index on the username and on the email fields. In mongoose it looks something like:
const userSchema = new Schema({
name: {
type: String,
index: {
unique: true
}
}
});
Or if you want to allow same usernames but the uniqueness to be the username-email combo, make a compound index.
And even better - move the whole thing to another file. Call the file user-registration.service.js. Make the file export one function:
async function registerUser(username, email, password) {
// check uniqueness here. if it fails, _throw an error!_
// if all is ok, save the user, return the details.
}
module.exports = {
registerUser
}
That way your route controller can just say:
router.post(
"/",
[
check("email", "Please, include a valid email.").isEmail(),
check(
"password",
"Please, enter a password with 6 or more characters"
).isLength({ min: 6 }),
check("username", "Please, include a valid username.")
.not()
.isEmpty()
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const {
email,
password,
birthdate,
birthplace,
sun,
username,
date,
is_paying
} = req.body;
try {
const user = await registerUser();
return res.json(user); // or token or whatnot.
} catch (err) {
// see here? only one place to deal with that error.
return res.status(400)
.json(err);
}
}
Now you only have that one place to deal with errors. You could even push it further and simply omit that try/catch and let a global error handler deal with it.
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const {
email,
password,
birthdate,
birthplace,
sun,
username,
date,
is_paying
} = req.body;
try {
// See if user exists
let user = await User.findOne({ $or: [{ username }, { email }] });
if (user) {
res.status(400).json({ errors: [{ msg: "User already exists" }] });
}
else{
// Create new user from User Model
user = new User({
email,
password,
birthdate,
birthplace,
sun,
username,
date,
is_paying
});
// Encrypt password
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(password, salt);
// Add user to database
await user.save();
// Return JWT
const payload = {
user: {
id: user.id
}
};
jwt.sign(
payload,
config.get("jwtSecret"),
{ expiresIn: 360000 },
(err, token) => {
if (err) throw err;
res.json({ token });
}
);
}
}
catch (err) {
console.error(err.message);
res.status(500).send("Server Error");
}
}
);
Add the add new user code in else section

How to make the return values of findOrCreate available to router

So in my express app I have a separate file database.js, which contains all the models and functions for inserting, deleting, updating etc. I also have a separate controller for each model.
database.js
module.exports = {
createUser: function (username, email, password) {
return sequelize.sync().then(function () {
User.findOrCreate({
where: {
username: name,
email: email
},
defaults: {
username: username,
password: password,
email: email
}
}).then(([user, created]) => {
console.log(user.get({plain:true}));
console.log(created)
});
});
}
};
controllers/user.js
const database = require("../database.js");
module.exports = {
register: function (req, res) {
database.createUser(req.body.username, req.body.email, req.body.password);
res.json({...
})
}
};
So basically I wanna get the user object and the boolean that tells me if it was created to the router so I can check if the user was created and make an appropriate response.
You could remove then in createUser and resolve the Promise when calling the function:
module.exports = {
createUser: function (username, email, password) {
return User.findOrCreate({
where: {
username: name,
email: email
},
defaults: {
username: username,
password: password,
email: email
}
})
};
The promise will then resolve into an array with a user object and a boolean you're looking for
const database = require("../database.js");
module.exports = {
register: function (req, res) {
database.createUser(req.body.username, req.body.email, req.body.password)
.then((result) => {
const [ object, created ] = result;
res.json({ user_is_created: created })
})
}
};
It's not a complete solution, you might have to explore edge cases and errors, but that's basically how you'd do it.

Login verification and Password recovery with JWT

I'm working on a web app and I wanna try using JWT for my user authentication and password recovery. I need JWT to verify the valid user from my DB. I tried this:
login.js:
var User = require("../model/user/registerSchema").User;
var bcrypt = require('bcrypt');
var utils = require('../util/util'),
config = require('../config'),
jwt = require('jsonwebtoken');
/* Login Route. */
route = (app)=>{
//POST
app.post ('/login', (req, res) => {
let {userName, password} = req.body;
if (!utils.noEmptyParams(req.body)) res.json({success: false, message: config.messages.NO_DATA});
else
User.findOne({userName, password}, {password: 0})
.exec()
.then(user => {
if (user) {
user = JSON.parse(JSON.stringify(user));
jwt.sign(user, config.jwt.secret, config.jwt.options, (err, token) => {
res.json({
success: true,
user,
token,
});
});
} else {
res.json({success: false, message: config.messages.INVALID_CREDENTIALS});
}
})
.catch(error => {
utils.error(error);
res.json({success: false});
});
});
}
module.exports.route = route;
registerSchema.js
var mongoose = require("mongoose"),
db = global.db,
bcrypt = require('bcrypt');
var User = new mongoose.Schema();
// MONGOOSE MODEL CONFIGURATION
const RegisterSchema = new mongoose.Schema({
access:{
type: String,
required:[true, 'please select proffession']
},
phone: {
type: String,
required: [true, 'Please add a username'],
unique: true
},
firstName: {
type: String,
required: [true, 'Please enter your firstname']
},
lastName: {
type: String,
required: [true, 'Please add your last name']
},
password: {
type: String,
required: [true, 'Please add a password']
},
userName: {
type: String,
required: [true, 'Please add an email address'],
unique: true
},
companyName: {
type: String
},
});
RegisterSchema.pre('save', function(next){
var user = this;
bcrypt.hash(user.password, 10, function(err, hash){
if(err){
return next(err);
}
user.password= hash;
next()
})
})
module.exports = mongoose.model('User', RegisterSchema);
I am running on Node version: v9.4.0, I'm using postman to test. When I try posting the require fields,
{
"userName": "njksdnf#fds.com",
"password": "1234567"
}
I got this error:
TypeError: Cannot read property 'findOne' of undefined
at app.post (/home/user/Home/Node/Routers/login.js:19:18)
I have seen WT-user-authentication-API-bolilerplate
, but it doesn't seem to help fully.
Any idea on how I can resolve it and how JWT can be used in this case for password recovery?
For the
TypeError: Cannot read property 'findOne' of undefined
at app.post (/home/user/Home/Node/Routers/login.js:19:18)
gotten from postman, all thanks to Striped in the comment. removing the.User did the whole magic for me.
Making my code to now be:
var User = require("../model/user/registerSchema");
instead of:
var User = require("../model/user/registerSchema").User;
Still left with the question "Any idea on how JWT can be used in this case for password recovery?

Cannot register with password encrypt

i follow this question but i still didn't figure out how to solve it, it says to use instanceMethods option to use the methods in the model thats exactly what i did:
"use strict";
var sequelize = require('./index');
var bcrypt = require('bcrypt-nodejs');
module.exports = function (sequelize, DataTypes) {
var User = sequelize.define("User", {
username: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING
}, {
instanceMethods: {
generateHash: function (password) {
console.log("hi");
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
},
validPassword: function (password) {
return bcrypt.compareSync(password, this.password);
}
},
});
return User;
}
after i use that i tried to test it generating a password hash on my register route like this:
var express = require('express');
var User = require('../../models').User;
var router = express.Router();
/* GET users listing. */
router.post('/', function (req, res, next) {
if (JSON.stringify(req.body) == "{}") {
return res.status(400).json({ Error: "Register request body is empty" });
}
if (!req.body.email || !req.body.username || !req.body.password) {
return res.status(400).json({ Error: "Missing fields for registration" });
}
var password = User.instaceMethods.generateHash(req.body.password);
User.create({
username: req.body.username,
email: req.body.email,
password: password
}).then(function () {
return res.status(200).json({message: "user created"});
})
});
module.exports = router;
with this when i go to my /register, i don't get any response even a console.log in the password doesn't show me anything i tried to see if it enters the generetehash but, it doesn't enter the method, what is going wrong here?
Add your generateHash as a classMethod of your model, not an instance method.
var User = sequelize.define('user', {
....
}, {
classMethods: {
generateHash: function() {
...
}
})
Because you don't have an instance yet, not before User.create().
Then you can call it using User.generateHash()
And also you should catch (and probably at least log) sequelize exceptions. Add
.catch(function(err){
console.log(err)
})
after your then()

Categories