Node.js passport authentication ignores controller function - javascript

If passport returns the user { status: 200 }:
passport.js
...
return done(null, rows[0], { status: 200 });
...
I want the controller 'controllerLogin.login' to be called:
routs/index.js
const express = require('express');
const passport = require('passport');
const passportConf = require('../passport');
const controllerLogin = require('../controllers/login');
...
router.route('/v1/login')
.post( function(req, res, next) {
passport.authenticate('local-login', function (err, user, context = {}) {
if (err) {
console.log(err);
}
if (context.status === 429) {
return res.status(429).send({ status: 429, success: false })
}
if (context.status === 401){
return res.status(401).send({ status: 401, success: false })
}
next();
//return;
})(req, res, next);
}, controllerLogin.login );
But I can't reach the controller 'controllerLogin.login'. What am I missing and how to execute 'controllerLogin.login'?
The below was working, but I need the upper version.
const passLogin = passport.authenticate('local-login', { session: false, failWithError: true });
router.route('/v1/login')
.post( passLogin, function(err, req, res, next) {
return res.status(401).send({ status: 401, success: false })
}, controllerLogin.login );
Edit: What works ...
router.route('/v1/login')
.post( function(req, res, next) {
passport.authenticate('local-login', { session: false, failWithError: false }, function (err, user, context = {}) {
if (err) {
console.log(err);
}
if (context.statusCode === 429) {
return res.status(429).send({ status: 429, success: false, message: { name: 'Rate Limit Error' } })
}
if (context.statusCode === 401){
return res.status(401).send({ status: 401, success: false, message: { name: 'Authentication Error' } })
}
// this works getting user information
console.log('user:');
console.log(user);
next();
})(req, res, next);
}, /*controllerLogin.login*/ (req, res) => { res.status(200).json({just: 'testing'})} );
controller/login.js
module.exports = {
login: async (req, res, next) => {
// Can't access user information via 'req.user' anymore
console.log('req.user:');
console.log(req.user);
/* .. How to access user information here? .. */
res.status(200).json({just: 'testing'})
}
}

It sounds like controllerLogin.login wants req.user, but that is not being set. So try doing that manually in the callback you pass in to passport's authenticate function.
router.route('/v1/login')
.post( function(req, res, next) {
passport.authenticate('local-login', { session: false, failWithError: false }, function (err, user, context = {}) {
if (err) {
console.log(err);
return next(err); // might want to add this line to handle errors?
}
if (context.statusCode === 429) {
return res.status(429).send({ status: 429, success: false, message: { name: 'Rate Limit Error' } })
}
if (context.statusCode === 401){
return res.status(401).send({ status: 401, success: false, message: { name: 'Authentication Error' } })
}
if(!user) {
// might want to handle this separately? user not found?
//return next('User not found');
}
// this works getting user information
console.log('user:');
console.log(user);
req.user = user;
next(); // this moves us on to controllerLogin.login
})(req, res, next);
}, controllerLogin.login);
Then in controller/login
module.exports = {
login: (req, res) => {
// remove user logging once this works, don't want to log sensitive info (!)
console.log('req.user in controller/login:')
console.log(req.user)
// user logic here
res.status(200).json({status: 200, success: true})
}
}
It's worth taking a look at passportjs docs under the "Custom Callback" section. That example doesn't pass along to another function as you are doing, but it can help to see another approach.

Related

Error cannot set headers after they are set to client

Im creating and employee leave managment system.
Everything works fine until when I try to update the leave status by admin and th logged in acc or user is not roled as admin.
Ive a middleware that checks the authentication of user and role of user
when an employeee tries to access this route it crashes the server logging cannot set headers already set to client and then ive to restart the server and access the route again
here is my Auth code:
const jwt = require("jsonwebtoken");
const employeeModels = require("../models/employeeModels");
exports.isAuthenticated = async (req, res, next) => {
try {
const { token } = req.cookies;
if (!token)
return res
.status(400)
.json({ success: false, error: "Please Login First" });
const decodedData = jwt.verify(token, "njuebuipoqdjbcibwjcnowdopq");
req.employee = await employeeModels.findById(decodedData.id);
next();
} catch (e) {
res.status(500).json({
success: false,
error: e.message,
});
next();
}
};
exports.AuthorizeRoles = (...role) => {
return (req, res, next) => {
if (!role.includes(req.employee.role)) {
res.status(400).json({
success: false,
error: "Only Admin is Allowed to Access this route",
});
}
next();
};
};
here is my leave controller
exports.createLeave = async (req, res, next) => {
try {
const { reason, noOfDays } = req.body;
const leave = await (
await LeaveModel.create({
reason,
noOfDays,
employee: req.employee._id,
})
).populate({
path: "employee",
});
res.status(200).json({
success: true,
message: "leave Has Been Submitted Successfully",
leave,
});
} catch (e) {
res.status(400).json({
success: false,
error: e.message,
});
next();
}
};
//get All Leaves For Admin
exports.getAllLeaves = async (req, res, next) => {
try {
const leaves = await LeaveModel.find().populate("employee");
if (leaves)
return res.status(200).json({
success: true,
leaves,
});
} catch (e) {
res.status(401).json({
success: false,
error: e.message,
});
next();
}
};
exports.updateLeaveStatus = async (req, res, next) => {
try {
const leave = await LeaveModel.findByIdAndUpdate(req.params.id, req.body, {
new: true,
runValidators: true,
});
if (leave)
return res.status(200).json({
success: true,
message: "Leave Status Updated Successfully",
leave,
});
} catch (e) {
return res.status(401).json({
success: false,
error: e.message,
});
next();
}
};
exports.deleteLeave = async (req, res) => {
try {
const leave = await LeaveModel.findByIdAndDelete(req.params.id);
if (leave)
return res.status(200).json({
success: true,
message: "Leave Deleted Successfully",
});
} catch (e) {
res.status(401).json({
success: false,
error: e.message,
});
next();
}
};
//controller for getting single leave detail
exports.getSingleLeave = async (req, res) => {
try {
//getting Leave From The Leave Model By Passing Id Fetched From The Req Url Param
const leave = await LeaveModel.findById(req.params.id).populate("employee");
if (leave)
return res.status(200).json({
success: true,
leave,
});
} catch (e) {
res.status(401).json({
success: false,
error: e.message,
});
next();
}
};
here are my leave routes
const {
createLeave,
getAllLeaves,
updateLeaveStatus,
deleteLeave,
getSingleLeave,
} = require("../controllers/leaveController");
const {
isAuthenticated,
AuthorizeRoles,
} = require("../middleware/Authentication");
const Leaverouter = express.Router();
Leaverouter.post("/createleave", isAuthenticated, createLeave)
.get(
"/admin/getallleaves",
isAuthenticated,
AuthorizeRoles("admin"),
getAllLeaves
)
.patch(
"/admin/updateleave/:id",
isAuthenticated,
AuthorizeRoles("admin"),
updateLeaveStatus
)
.delete(
"/admin/deleteleave/:id",
isAuthenticated,
AuthorizeRoles("admin"),
deleteLeave
)
.get(
"/admin/leavedetails/:id",
isAuthenticated,
AuthorizeRoles("admin"),
getSingleLeave
);
module.exports = Leaverouter;
Do not call next() after sending a response (you have multiple places where you are doing this). You ONLY call next() when you want routing to continue so some other route handler can send a response. If you've already sent a response and then you call next() and some other route handler (or the default 404 route handler) gets called and tries to then send a second response, you will get this error.
The particular error you are getting is caused when your code is attempting to send more than one response to the same request.

req.isAuthenticated() never evaluates to true

I'm trying to implement user authentication as below:
userRouter.post("/login", passport.authenticate("local", { session: false }), (req, res) => {
if (req.isAuthenticated()) {
const { _id, username } = req.user;
const token = signToken(_id);
res.cookie("access_token", token, { httpOnly: true, sameSite: true });
res.status(200).json({ isAuthenticated: true, user: { username } });
}
});
But to be able to add some custom messages I'm using the alternative below:
userRouter.post('/login', (req, res, next) => {
passport.authenticate('local',{ session: false }, (err, user, info) => {
if (req.isAuthenticated()) {
const { _id, username } = req.user;
const token = signToken(_id);
res.cookie("access_token", token, { httpOnly: true, sameSite: true });
res.status(200).json({ isAuthenticated: true, user: { username } });
}
if (err) return next(err)
if (info)
res.status(400).json({ message: { msgBody: info.message, msgError: true } });
})(req, res, next);
});
However, in the second alternative, it seems like req.isAuthenticated() is never evaluated to true.
Can anyone help me understand why?
Thanks
The passport docs say "Note that when using a custom callback, it becomes the application's responsibility to establish a session (by calling req.login()) and send a response." (http://www.passportjs.org/docs/authenticate/)
With the follow code as an example:
app.get('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/login'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/users/' + user.username);
});
})(req, res, next);
});
I wonder if it says you are not authenticated because you need to call the login method?

passport-jwt returns unauthorized when I go to /admin page on my web app

I am trying to get the user logged in using passport and jwt token. I manage to generate jwt token successfully but when I go to /admin page by typing in the browser I am getting the unauthorized message. I have read all the answer here but they aren't helping me out.
My passport.js file
try {
passport.use('signin', new LocalStrategy(
function(username, password, done) {
Users.findOne({username: username}, (err, user) => {
if (err) { return(err)}
if(!user) { return done(null, false, { message: 'Incorrect username.' })}
if (!checkUser(password, user.password)) { return done(null, false, { message: 'Incorrect username.' }); }
return done(null, user);
});
}
))
} catch (error) {
return done(error);
}
passport.use(new JWTstrategy({
secretOrKey: config.get('jwt.tokenKey'),
jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken()
}, async(token, done) => {
try {
return done(null, token.user);
} catch (err){
done(err);
}
}))
My login-controller file: -
router.post('/', (req, res) => {
passport.authenticate('signin', {session:false}, async(err,user,info) => {
try {
if (err || !user) {
console.log(err);
}
req.login(user, {session:false}, err => {
if (err) res.send(err);
const body = { username: user.username};
const token = jwt.sign( {user:body}, config.get('jwt.tokenKey'));
res.json({ success: true,
token: 'Bearer ' + token });
});
} catch (error) {
return next(error);
}
})(req, res);
})
My admin page file: -
router.get('/', passport.authenticate('jwt', { session: false }), function (req,res) {
res.render('./../views/admin/admin.pug');
})

View is not change after render .pug file from express

I have some problem that the html view is not change after render the pug file,after getting the server response.
The code is following
app.set('view engine', 'pug');
app.set("views", require('path').join(__dirname, "views"));
app.post('/login', function(req, res, next) {
console.log(req.body);
checkExistanceOfuser(req.body.username, req.body.password, function(err, flag, data) {
console.log(err);
if (err) {
res.send({
err: 'SOME_DATABASE_ERROR'
})
} else {
if (flag) {
req.session.user = data;
/*res.send({
success: true,
message: 'Login Success'
})*/
res.render('welcome', { name: data.name });
} else {
/*res.send({
success: false,
message: 'Invalid Credentials'
});*/
res.render('login', { error: 'Invalid Credentials' });
}
}
})
But i check in network section from browser.The API response (preview) is fine.
when you are calling /login route it's a post call and probably you are using ajax post call for doing so.
Now when you are calling the /login route its rendering the pug file
but its not actually effecting the browser DOM. So what you need to do is this
create a new route like this
app.get('/login-success', function(req, res, next) {
if (req.session && req.session.user) {
res.render('welcome', { name: req.session.user.name });
} else {
res.redirect('/');
}
});
and modify the login function like this
app.post('/login', function(req, res, next) {
console.log(req.body);
checkExistanceOfuser(req.body.username, req.body.password, function(err, flag, data) {
console.log(err);
if (err) {
res.send({
err: 'SOME_DATABASE_ERROR'
})
} else {
if (flag) {
req.session.user = data;
res.send({
success: true,
message: 'Login Success'
});
} else {
res.send({
success: false,
message: 'Invalid Credentials'
});
}
}
})
});
in ajax call use this
function login(){
$.post("http://localhost:8000/login",
{
username:document.getElementById("name").value,
password:document.getElementById("password").value,
},
function(data, status){
if(data.success){
window.location = "http://localhost:8000/login-success"
}
});
}
You're rendering views/login, but you've already specified that the view folder is views. Just render login.

Why auth.isAuthenticated() is not working in MEAN.JS?

I have made simple signup, signin and article using MEAN.JS with jsonwebtoken.
In signup page after user entering all values i am passing values to server through signup api. The server side I am creating jsonwebtoken and am passing to client side
exports.create = function (req, res, next) {
var newUser = new User(req.body);
newUser.provider = 'local';
newUser.role = 'user';
newUser.save(function(err, user) {
if (err) return validationError(res, err);
var token = jwt.sign({
_id: user._id
}, config.secrets.session, {
expiresInMinutes: 60 * 5
});
res.json({
token: token
});
});
};
After getting that token client calling some 'me' api (I did not understand what is that me is passing)
client side signup controller:
$scope.register = function(form) {
Auth.createUser({
username: $scope.user.name,
useremail: $scope.user.email,
password: $scope.user.password
})
};
auth.service:
createUser: function(user, callback) {
var cb = callback || angular.noop;
return User.save(user,
function(data) {
$cookieStore.put('token', data.token);
currentUser = User.get();
return cb(user);
},
function(err) {
this.logout();
return cb(err);
}.bind(this)).$promise;
}
user.service :
.factory('User', function ($resource) {
return $resource('/api/users/:id/:controller', {
id: '#_id'
},
{
changePassword: {
method: 'PUT',
params: {
controller:'password'
}
},
get: {
method: 'GET',
params: {
id:'me'
}
}
});
});
After signup:
get: {
method: 'GET',
params: {
id:'me'
}
}
I did not understand this. In server side 'me' api looking like this
route:
router.get('/me', auth.isAuthenticated(), controller.me);
controller :
exports.me = function(req, res, next) {
var userId = req.user._id;
User.findOne({
_id: userId
}, '-salt -hashedPassword', function(err, user) {
if (err) return next(err);
if (!user) return res.status(401).send('Unauthorized');
res.json(user);
});
};
auth.service:
var validateJwt = expressJwt({ secret: config.secrets.session });
/**
* Attaches the user object to the request if authenticated
* Otherwise returns 403
*/
function isAuthenticated() {
return compose()
// Validate jwt
.use(function(req, res, next) {
// allow access_token to be passed through query parameter as well
if(req.query && req.query.hasOwnProperty('access_token')) {
req.headers.authorization = 'Bearer ' + req.query.access_token;
}
validateJwt(req, res, next);
})
// Attach user to request
.use(function(req, res, next) {
User.findById(req.user._id, function (err, user) {
if (err) return next(err);
if (!user) return res.status(401).send('Unauthorized');
req.user = user;
next();
});
}).use(function (err, req, res, next) {
if (err.name === 'UnauthorizedError') {
var e = [];
e.push(err);
return res.status(401).send(e);
}
});
}
I want to know what they are passing in the 'me' api and how I'm getting 'req.user._id' in exports.me function. If I want to make the 'me' api (my own), how can I pass this my token?
The server side console I'm getting this: GET /api/users/me 200 876ms - 339b.

Categories