Passport JS "Can't set headers after they are sent" - javascript

Getting this error when I successfully log in with passport JS. Trying to redirect to the home page once I log in.
Code that does it:
app.post('/login',
passport.authenticate('local', {failureRedirect: '/login' }),
function(req, res) {
res.redirect('/');
});
Full Error:
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (http.js:644:11)
Am I missing something? Not sure why this error is happening. I'm still able to use the app, I just dont want the error.

You are redirecting the user so serializeUser function is being called twice. And in
passport.use(new FacebookStrategy({
...
be sure to add this else or it gets called twice, thus sending the headers twice and causing error. Try this:
passport.use(new FacebookStrategy({
...
},
function(accessToken, refreshToken, profile, done) {
// asynchronous verification, for effect...
process.nextTick(function () {
// To keep the example simple, the user's Facebook profile is returned to
// represent the logged-in user. In a typical application, you would want
// to associate the Facebook account with a user record in your database,
// and return that user instead.
User.findByFacebookId({facebookId: profile.id}, function(err, user) {
if (err) { return done(err); }
if (!user) {
//create user User.create...
return done(null, createdUser);
} else { //add this else
return done(null, user);
}
});
});
}
));

According to the PassportJS guide, you're supposed to let their middleware do all of the redirects.
app.post('/login', passport.authenticate('local', { successRedirect: '/',
failureRedirect: '/login' }));
My guess is that the middleware is calling Express' res.redirect method just like you are in your example above, but has an error in its implementation (calling next when it shouldn't) and then your method is trying to call res.redirect again, and that causes the error to be thrown because you can only send a response to the client once in the HTTP protocol.

Related

req.user and Is Authenticated always false

I am using react with express.js (inside router/user.js)
router.get("/", function (req, res) {
console.log("this is u" , req.user)
console.log(req.isAuthenticated());
if (req.user) {
res.json({ user: req.user })
} else {
res.json({ user: 'does not exsist' })
}
});
Here the console.log show the value against them as always undefined
console.log("this is u" , req.user)
console.log(req.isAuthenticated());
The above code always console.log false, Now I went through the other examples where they mentioned the problem can be due to the way you put things in server.js(or app.js) and hence I checked and think that my problem is not because of that reason, Anyway this is how I am adding stuff
app.use(session({
secret: 'keyboard cat', // -> used to encode and decode the session, the seceret we used will be used to encode to decode
resave: true,
saveUninitialized: true,
}));
app.use(passport.initialize());
app.use(passport.session());
//Session
passport.use(new LocalStrategy(user.authenticate()));
passport.serializeUser(user.serializeUser());
passport.deserializeUser(user.deserializeUser());
Here is my complete proper repository if someone wants to view: https://github.com/irohitb/litifier-
Now, Can anyone please guide me about fixing this problem?
This is because the login process is not being executed.
This results into cookie not being sent, which results into cookie not being read in the next request, which results into req.isAuthenticated() returning false
From the passport.js docs,
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.
Solution:
As this looks like your signup page, I dont think you need to do passport.authenticate here. That is for login. Remove that.
After you register the new user, do the following:
req.logIn(user, function (err) {
if (err) {
return next(err);
}
return res.json(user) // send whatever you want or redirect
});
Try adding {credentials: 'include'} in the route
Example
get('/test', {credentials: 'include'})

Passport.js not calling LocalStrategy if fields are empty

I'm having a problem where if I try to login with no username or password my passport.use function isn't being called at all.
Below is my express post route that runs passport.authenticate.
app.post('/login', passport.authenticate('local-login', {
failureRedirect: '/login', // redirect back to the login page if there is an error
failureFlash: true // allow flash messages
})
And below is my passport.use that should print GOT HERE whenever there is a post request to /login.
passport.use('local-login', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) { // callback with email and password from our form
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
console.log("GOT HERE");
This works fine if email and password have some type of value. But I would like this function to be called even if there is no value for email and password so that I can do custom error handling.
How can I achieve this?
You could add middleware befure authentication strategy being called. Something like this:
app.post('/login', function(req, res, next) {
// do custom error handling
}, passport.authenticate('local-login', {
failureRedirect: '/login', // redirect back to the login page if there is an error
failureFlash: true // allow flash messages
})
And in this middleware you could do some custom error handling

Node express passport (JWT) - callback after auth

I have authentication working with passport JWT, but I am having difficulty running a callback function/response, only if a user is authenticated. How can this be done?
In my routes:
import express from 'express';
import passport from '../../config/passport';
import memberInfoCtrl from '../controllers/memberInfo';
const router = express.Router();
router.route('/members')
.get(membersCtrl.tokenCheck, passport.authenticate('jwt', { session: false }), membersCtrl.doSomethingElse);
export default router;
I want membersCtrl.doSomethingElse to be run if authentication is successful.
Here is my tokenCheck function:
function tokenCheck(req, res, next) {
const token = getToken(req.headers);
if (token) {
const decoded = jwt.decode(token, config.secret);
User.findOne({
email: decoded.email
}, (err, user) => {
if (err) throw err;
if (!user) {
return res.status(403).send({
success: false, msg: 'Authentication failed. User not found.'
});
}
// res.json({ success: true, msg: 'Welcome to the member area!' });
return next();
});
} else {
return res.status(403).send({ success: false, msg: 'No token provided.' });
}
}
Using res.json in tokenCheck works fine, but the doSomethingElse function is not getting called afterwards.
I thought it's because we send a response like this:
res.json({ success: true, msg: 'Welcome to the member area!' });
Replacing the res.json with return next(); returns the error:
Error: Unknown authentication strategy \"jwt\"
Why does this happen - how can I check for authentication before executing another function, for the same route?
I'm sure it's something silly i'm missing. Any help is appreciated!
Here is part of my main express script that initialises passport:
import passport from 'passport';
...
app.use(passport.initialize());
app.use('/api', routes);
...
Passport config:
const JwtStrategy = require('passport-jwt').Strategy;
import User from '../server/models/user';
import config from './env';
module.exports = (passport) => {
const opts = {};
opts.secretOrKey = config.secret;
passport.use(new JwtStrategy(opts, (jwtPayload, done) => {
User.findOne({ id: jwtPayload.id }, (err, user) => {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false);
}
});
}));
};
Your general approach is correct. To protect a route depending on some conditions write a custom middleware calling next() if those conditions are fulfilled.
But in your case this is not necessary as this is what passport-jwt does. So assuming that you configured passport and passport-jwt correctly all you need to write is this:
router.route('/members')
.get(passport.authenticate('jwt', { session: false }), membersCtrl.doSomethingElse);
passport-jwt will extract the JWT from the request and verify it against your provided secret or key. Afterwards it will use passport's verify callback to populate req.user (source).
Additionally: Yes after using res.json() a response is sent which is why your passport middleware and anything beyond is not reached in that case.
Regarding your error Error: Unknown authentication strategy \"jwt\": This usually happens if you did not configure your passport authentication correctly. If you include it in your question I will take a look at it and extend my answer accordingly.
Update: Your code looks good to me except one thing: You did not specify the jwtFromRequest attribute in your passport-jwt options which is mandatory. Or did you by any chance forget to invoke your passport config?
Update 2: Further clarification regarding my comment below: 1.) Import your ES6 passport config module (where you added the jwtFromRequest option) in your main express script and invoke it:
import passport from 'passport';
import passportConfig from 'path/to/passport/config';
...
app.use(passport.initialize());
passportConfig(passport);
...
2.) Make sure to remove your tokenCheck function, you don't need it. See the first part of this answer.
Update 3: 'Unauthorized' is great because it means that you are successfully protecting your /members route now. To implement a token-based authentication you now need an additional route /authenticate where users can request access to your web service e.g. by providing credentials (your route will verify these credentials and respond with a JWT which must be signed with the same secret you are using for passport-jwt - otherwise passport-jwt will not be able to verify the token signature later).
But this goes beyond the scope of this question. There should be many resources out there covering this. You can for example implement an /authenticate route using jsonwebtoken which is shown in this article (they are not using passport at all but use jsonwebtoken for token creation and validation).
Note: If you are also implementing a client you have to include some additional logic there to store the JWT and to include it in your requests to the express app.

NodeJs what happen when Token is correct on next() method?

i'm newbie in using Authenticate a Node.js API with JSON Web Tokens, i read this document on scotch.io but i cant understand when Token is correct what happen after next() method
apiRoutes.use(function(req, res, next) {
// check header or url parameters or post parameters for token
var token = req.body.token || req.param('token') || req.headers['x-access-token'];
// decode token
if (token) {
// verifies secret and checks exp
jwt.verify(token, app.get('superSecret'), function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
// if everything is good, save to request for use in other routes
req.decoded = decoded;
next();
}
});
} else {
...
}
});
apiRoutes.get('/', function(req, res) {
...
});
apiRoutes.get('/users', function(req, res) {
...
});
apiRoutes.get('/check', function(req, res) {
...
});
app.use('/api', apiRoutes);
app.listen(port);
User must be pass other request after that (my mean is after token is correct)?
i think in this sample code / route will be call after check token and it was correct,ok? how can i choose other method to call, because / is calling after next()
actually next() asking for nodejs to go on for next step it's whatever in node.js async loop. Javascript actually single threaded so next will call whatever method placed at stack. if you will not call next() your program will stuck. and will not call any other method after this token middleware which you have passed to .use
In this particular scenario it will call the next route after verified by this token middleware, which you are passing as a anonymous function.
also have a look at this method
javascript node.js next()

MEAN Stack login/register in different page than main app

Sorry for my bad English!
I build an app using MEAN Stack (MongoDB + Express.js + Angular.js + Node.js) and authentication using passport.js and JWT (jsonwebtoken and express-jwt).
What i want to do?
The login and registration routes render html, using nunjucks, and if it goes success we render index.html file, placing in public directory.
P.S.: is this a correct method?
What is my question?
Can anyone tell me, the technology to do this?
My code (based on https://thinkster.io/mean-stack-tutorial):
app.get('/', function(req,res){
if (!req.user)
res.redirect('/about')
else
res.render('index')
});
app.get('/login',function(req, res) {
res.render('auth/login', {
title: 'Login'
});
});
app.post('/', function(req, res, next) {
if (!req.body.login || !req.body.password) {
return res.status(400).json({ message: 'Please fill out all fields' });
}
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (user) { return res.json({ token: user.generateJWT()});
/*generateJWT form user model code returns jwt.sign({}) using jsonwebtoken:
var jwt = require('jsonwebtoken');
*/
} else { return res.status(401).json(info);}
})(req, res, next);
});
In this code we return generated token, if login is success. What should i do to render index.html and what could i do with generated token?
Sorry for this dummy questions, i'm still learning jwt. Thank's
Node is a Server Side Language. Since you are using MEAN stack, generally,
you need to create APIs in node and pass datas to client side Angular.
So to answer your question- To render html, your function for login needs to return something to client, using callbacks, and Angular will render your html pages.
What you need to do with the generated token is that from next time any requests to server without the token will/should generate error.
Else anyone can call your node APIs and and desired results. This is extremely necessary atleast for GET methods.

Categories

Resources