I'm trying to build some auth and after successful auth I can't access values inside object inside object in mongodb, it returns undefined. Is there any thing I need to know about mongodb objects?
Here's my code:
app.post('/user/auth', function (req, res) {
// find the user
console.log('requested stuff is ' + req.body.username + ' and ' + req.body.password)
User.find({
'data.username': req.body.username,
'data.password': req.body.password
}, function (err, user) {
console.log(user) // returns { _id: 59085883734d1d3098a83590, data: { username: 'admin1', password: 'password', email: 'email' }, profile: { nickname: 'workingNickname' } }
console.log(user.data) // returns undefined
console.log(user['data']) // returns undefined
if (err) throw err;
if (!user) {
res.send('User not found with' + req.body.username);
} else if (user) {
// check if password matches
if (user.password != req.body.password) {
res.send('Password wrong. Auth failed');
} else {
// if user is found and password is right
// create a token
var token = jwt.sign(user, app.get('superSecret'), {
expiresIn: 60 * 60 * 24 // expires in 24 hours
});
// return the information including token as JSON
res.json({
success: true,
message: 'Enjoy your token!',
token: token
});
}
}
});
});
Related
I have this code for login. How do I use the current user's information from this code to another file using postman and node.js?
exports.loginUser = (req,res, next) => {
User.find({email: req.body.email})
.exec()
.then(user => {
if(user.length < 1) {
return res.status(401).json({
message: 'Auth failed'
});
}
bcrypt.compare(req.body.password, user[0].password, (err ,result) => {
if(err){
return res.status(401).json({
message: 'Auth failed'
});
}
if (result) {
const token = jwt.sign({
email: user[0].email,
userId: user[0]._id
},
process.env.JWT_KEY ,
{
//options
expiresIn: "1h"
});
You should tell exactly what you want, what you said is confusing, but If you mean how to pass the logged in user to the next middleware, you gotto assign the user to req
exports.loginUser = async (req, res, next) => {
const user = await User.find({ email: req.body.email }).exec()
if (user.length < 1) {
return res.status(401).json({
message: 'Auth failed'
});
}
bcrypt.compare(req.body.password, user[0].password, (err, result) => {
if (err) {
return res.status(401).json({
message: 'Auth failed'
});
}
if (result) {
const token = jwt.sign({
email: user[0].email,
userId: user[0]._id
},
process.env.JWT_KEY, {
//options
expiresIn: "1h"
});
req.user = user[0];
return next();
}
})
}
Then in the next middleware you have access to logged in user, using req.user.
UPDATE:
To implement the functionality that you want, according to what you described in the comment:
Before anything import these packages:
const jwt = require("jsonwebtoken");
const { promisify } = require("util");
First you implement a route that checks for credentials and sends back a signed jwt:
exports.login = CatchAsync(async(req, res, next) => {
const { email, password } = req.body;
if (!email || !password) {
return next(new Error("Please provide email and password"));
}
const user = await UserModel.findOne({email});
if (!user) {
return next(new Error("There is no user with that email"));
}
if(!(await bcrypt.compare(password, user.password))) {
// actually the pass is not correct but for security reasons we don't say that
return next(new Error("Email or password is not correct");
}
// pass the user id to jwt so later can identify user
const token = jwt.sign({ id: user._id }, 'yourJwtSecret', {
expiresIn: '90d',
});
// httpOnly prevents access to token in client's browser, so it is safe
const cookieOptions = {
expires: new Date(
Date.now() + 90 * 24 * 60 * 60 * 1000
),
httpOnly: true,
};
res.cookie("jwt", token, cookieOptions);
res.status(200).json({
status: 'success',
message: 'logged in successfully'
});
});
Then for every route that needs to check for logged In user, use this middleware:
exports.isLoggedIn = CatchAsync(async(req, res, next) => {
// Check if there is a token
// if no token was provided it means user is not logged in
let token;
if (req.cookies.jwt) {
token = req.cookies.jwt;
} else {
return next();
}
// Verify token
// decoded now has access to id of user
let decoded;
try {
decoded = await promisify(jwt.verify)(token, 'yourJwtSecret');
} catch (err) {
// if token was modified or expired or not valid
return next();
}
// get the user
const user = await UserModel.findOne({
_id: decoded.id
});
// access granted, user is logged in
req.user = user; // you can access the logged in user in the next middleware
res.locals.user = user; // you can access the logged in user in template engines
next();
});
If the user is not logged in, req.user won't be assigned. therefore in next middlewares if req.user was undefined you know user is not logged in.
for more info jwt docs.
If you have never taken any NodeJs course, I'd recommend this course
I am using a JWT token based login system and it is working fine.
But I need to get user details based on JWT token
exports.signin = function(req, res) {
User.findOne({
username: req.body.username
}, function(err, user) {
if (err) throw err;
if (!user || !user.comparePassword(req.body.password)) {
return res.status(401).json({ message: 'Authentication failed. Invalid user or password.' });
}
return res.json({ token: jwt.sign({ email: user.email, username: user.username, _id: user._id }, 'RESTFULAPIs') });
});
};
app.use(function(req, res, next) {
if (req.headers && req.headers.authorization && req.headers.authorization.split(' ')[0] === 'JWT') {
jsonwebtoken.verify(req.headers.authorization.split(' ')[1], 'RESTFULAPIs', function(err, decode) {
if (err) req.user = undefined;
req.user = decode;
next();
});
} else {
req.user = undefined;
next();
}
});
I also need to set the expiration time.
How can I do that?
Ref : NodeJs - Retrieve user infor from JWT token?
exports.me = function(req,res){
if (req.headers && req.headers.authorization) {
var authorization = headers.authorization,
decoded;
try {
decoded = jwt.verify(authorization, secret.secretToken);
} catch (e) {
return res.status(401).send('unauthorized');
}
var userId = decoded.id;
// Fetch the user by id
User.findOne({_id: userId}).then(function(user){
// Do something with the user
return res.send(200);
});
}
return res.send(500);
}
Ref: For token expiration / extending it JWT (JSON Web Token) automatic prolongation of expiration
Web applications
A good pattern is to refresh the token before it expires.
Set the token expiration to one week and refresh the token every time the user open the web application and every one hour. If a user doesn't open the application for more than a week, they will have to login again and this is acceptable web application UX.
To refresh the token your API needs a new endpoint that receives a valid, not expired JWT and returns the same signed JWT with the new expiration field. Then the web application will store the token somewhere
Modify Your code as follow:-
exports.signin = function (req, res) {
User.findOne({
username: req.body.username
}, function (err, user) {
if (err) throw err;
if (!user || !user.comparePassword(req.body.password)) {
return res.status(401).json({ message: 'Authentication failed. Invalid user or password.' });
}
let NumberOfDayInMiliSec = 1000 * 60 * 60 * 24 * 1 //One Day
return res.json({ token: jwt.sign({ exp: Date.now() + NumberOfDayInMiliSec, email: user.email, username: user.username, _id: user._id }, 'RESTFULAPIs') });
});
};
app.use(function (req, res, next) {
if (req.headers && req.headers.authorization && req.headers.authorization.split(' ')[0] === 'JWT') {
jsonwebtoken.verify(req.headers.authorization.split(' ')[1], 'RESTFULAPIs', function (err, decode) {
if (err) req.user = undefined;
if (decode.exp < Date.now()) {
return res.status(400).json({ status: false, msg: "Token expired" });
}
req.user = decode;
next();
});
} else {
req.user = undefined;
next();
}
});
I am writing a sign-in function with my express app and do not like the fact that in the callback chain, lots of res.status(500).send(body) are duplicated:
router.post('/login', (req, res) => {
User.findOne({
where: { username: req.body.username }
})
.then( user => {
if (user) {
User.verifyPassword(req.body.password, user)
.then((verified) => {
if (verified) {
let signedToken = jwt.sign(
{ user: user.id },
'secret',
{ expiresIn: 24 * 60 * 60 }
);
res.status(200).send({
token: signedToken,
userId: user.id,
username: user.username
});
} else {
// If password entered does not match user password
res.status(500).send({ error: true, });
}
})
// If bycrpt explodes
.catch((error) => {
res.status(500).send({ error: error, });
});
} else {
// If we can't even find a user with that username
res.status(500).send({ error: true, });
}
})
// If the db query to find a user explodes
.catch(error => {
res.status(500).send({ error: error });
});
});
Two of these are related to vague exceptions occurring that make the API blow up. The other two are based on boolean values returned by the APIs.
I am not much of a back end engineer and this is just a personal project, but I want to know what are the best practices for this in the Node.js world.
While we're at it, I'm not sure what the appropriate status code to send in these error cases would be, as I am sure 500 is not correct.
I would rewrite your code like this, where we only have one .catch
router.post('/login', (req, res) => {
User.findOne({ where: { username: req.body.username }})
.then(user => {
if (!user) // If we can't even find a user with that username
return Promise.reject(true); // You should probably return an Error
return User.verifyPassword(req.body.password, user)
})
.then((verified) => {
if (!verified) // If password entered does not match user password
return Promise.reject(true); // You should probably return an Error
let signedToken = jwt.sign({
user: user.id
},
'secret', {
expiresIn: 24 * 60 * 60
}
);
res.status(200).send({
token: signedToken,
userId: user.id,
username: user.username
});
}).catch(error => {
// If the db query to find a user explodes
// If we can't even find a user with that username
// If password entered does not match user password
// You could throw different errors and handle
// all of them differently here
res.status(500).send({
error: error
});
});
});
This can be improved a little bit further, using async/await
router.post('/login', async(req, res) => {
try {
const user = await User.findOne({ where: { username: req.body.username }});
if (!user) // If we can't even find a user with that username
throw new Error('Invalid username');
const verified = await User.verifyPassword(req.body.password, user)
if (!verified) // If password entered does not match user password
throw new Error('Invalid password');
let signedToken = jwt.sign({
user: user.id
},
'secret', {
expiresIn: 24 * 60 * 60
}
);
res.status(200).send({
token: signedToken,
userId: user.id,
username: user.username
});
} catch(error) {
// If the db query to find a user explodes
// If we can't even find a user with that username
// If password entered does not match user password
res.status(500).send({
error: error.message
});
}
});
Regarding the status code, there are multiple ways to handle them, I usually throw a specific error for each status code.
errors.js
class Unauthorized extends Error {
constructor(message) {
super(message);
this.name = 'UnauthorizedError';
this.statusCode = 401
}
}
class BadRequest extends Error {
constructor(message) {
super(message);
this.name = 'BadRequestError';
this.statusCode = 400
}
}
/** more errors... **/
module.exports = {
Unauthorized,
BadRequest
};
So we can now set the right status code:
const { Unauthorized } = require('./errors');
/* ... */
try {
/* ... */
if (!verified) // Some people may say 422 status code...
throw new Unauthorized('Invalid password');
/* ... */
} catch(error) {
res.status(error.statusCode || 500).send({
error: error.message
});
}
While we're at it, I'm not sure what the appropriate status code to
send in these error cases would be, as I am sure 500 is not correct.
You're right that setting 500 for every error is not correct. I'll leave you a couple of questions that might help you set the correct status code, since it will be too long to discuss it in this question.
What's an appropriate HTTP status code to return by a REST API service for a validation failure?
What's the appropriate HTTP status code to return if a user tries logging in with an incorrect username / password, but correct format?
I am learning how to use NodeJS, and while following an online tutorial I incurred in the error in the title.
I am using the latest version of NodeJS, and the jsonwebtoken authentication.
This is how I generate the token:
router.post('/login', (req, res) => {
// Check if username was provided
if (!req.body.username) {
res.json({
success: false,
message: 'No username was provided'
}); // Return error
} else {
// Check if password was provided
if (!req.body.password) {
res.json({
success: false,
message: 'No password was provided.'
}); // Return error
} else {
// Check if username exists in database
User.findOne({
username: req.body.username.toLowerCase()
}, (err, user) => {
// Check if error was found
if (err) {
res.json({
success: false,
message: err
}); // Return error
} else {
// Check if username was found
if (!user) {
res.json({
success: false,
message: 'Username not found.'
}); // Return error
} else {
const validPassword = User.comparePassword(req.body.password, user.password); // Compare password provided to password in database
// Check if password is a match
if (!validPassword) {
res.json({
success: false,
message: 'Password invalid'
}); // Return error
} else {
const token = jwt.sign({
userId: user._id
}, 'goodsecret', {
expiresIn: '24h'
}); // Create a token for client
res.json({
success: true,
message: 'Success!',
token: token,
user: {
username: user.username
}
}); // Return success and token to frontend
}
}
}
});
}
}
});
And here how I evaluate it:
const jwt = require('jsonwebtoken');
/* ================================================
MIDDLEWARE - Used to grab user's token from headers
================================================ */
router.use((req, res, next) => {
const token = req.headers.authorization; // Create token found in headers
// Check if token was found in headers
if (!token) {
res.json({
success: false,
message: 'No token provided'
}); // Return error
} else {
// Verify the token is valid
console.log(token);
jwt.verify(token, 'goodsecret', (err, decoded) => {
// Check if error is expired or invalid
if (err) {
res.json({
success: false,
message: 'Token invalid: ' + err
}); // Return error for token validation
} else {
req.decoded = decoded; // Create global variable to use in any request beyond
next(); // Exit middleware
}
});
}
});
/* ===============================================================
Route to get user's profile data
=============================================================== */
router.get('/profile', (req, res) => {
// Search for user in database
User.findOne({
_id: req.decoded.userId
}).select('username email').exec((err, user) => {
// Check if error connecting
if (err) {
res.json({
success: false,
message: err
}); // Return error
} else {
// Check if user was found in database
if (!user) {
res.json({
success: false,
message: 'User not found'
}); // Return error, user was not found in db
} else {
res.json({
success: true,
user: user
}); // Return success, send user object to frontend for profile
}
}
});
});
Whenever I try to generate a request from HttpRequester, sending the token, I get the error in the title. The token I am sending is
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI1OWRlMTYzNTMwOWY2NDM1YjBjOWRmM2UiLCJpYXQiOjE1MDc3NDMzODYsImV4cCI6MTUwNzgyOTc4Nn0.uJwpYN7IHYg_lmCVCpFg-zfo0QVPglEvWHs7SD9cPkg
and on https://jwt.io/ it works fine, using the secret 'goodsecret' as in the code. What am I missing?
This is a screen on how I am generating requests.
Thank you for your help
I have a route that I must authenticate by getting sequelize to search for any instance of an existing username and password in the request body in order to know if I can go ahead and authenticate the user. I'm not sure how to do this (search and check instances in Sequelize) because documentation is pretty new for Sequelize. I attempted User.findOne by reading here http://docs.sequelizejs.com/en/latest/api/model/ for example
db.User.findOne({
where: { password: req.password, username: req.username }
}).then(function(address) {
if(address.User !== null) {
console.log('User name: '+address.User.name);
} else {
console.log('User name: NO USER');
}
But not sure of the exact syntax/approach. I have something like this:
app.post('/authenticate', function (req, res) {
//TODO validate req.body.username and req.body.password
//if is invalid, return 401
if (!(req.body.username === 'john.doe' && req.body.password === 'foobar')) {
res.status(401).send('Wrong user or password');
return;
}
var profile = {
first_name: 'John',
last_name: 'Doe',
email: 'john#doe.com',
id: 123
};
// We are sending the profile inside the token
var token = jwt.sign(profile, secret, { expiresIn: 18000 });
res.json({ token: token });
});
app.get('/api/restricted', function (req, res) {
console.log('user ' + req.user.email + ' is calling /api/restricted');
res.json({
name: 'foo'
});
});