Is there a way to use facebookResponse as a pointer function, rather than using a anon function, and have it access req, res, next? I thought about using bind but I don't know how I would bind the .get() method of the route. I didn't have success with passport.authenticate('facebook',facebookResponse.call(this, req, res, next).
auth.route('/auth/facebook/callback')
.get(function(req, res, next) {
passport.authenticate('facebook', facebookResponse);
});
function facebookResponse(err, userDoc, info) {
if (err) { return next(err); }
// I don't think !userDoc will ever happen because of mongo upsert
if (!userDoc) { return res.redirect('/login'); }
req.logIn(userDoc, function(err) {
if (err) { return next(err); }
return res.redirect('http://localhost:9000/users');
});
}
One way would be to use a function to return a function:
auth.route('/auth/facebook/callback')
.get(function(req, res, next) {
passport.authenticate('facebook', facebookResponse(req, res, next));
});
function facebookResponse(req, res, next) {
return function(err, userDoc, info) {
if (err) { return next(err); }
// I don't think !userDoc will ever happen because of mongo upsert
if (!userDoc) { return res.redirect('/login'); }
req.logIn(userDoc, function(err) {
if (err) { return next(err); }
return res.redirect('http://localhost:9000/users');
});
}
}
Here your inner function has access to the outer functions parameters (req, res, next) in a closure.
Related
// state edit route
app.get("/map/:symbol/edit", isLoggedIn, function(req, res){
State.findOne({symbol: req.params.symbol}, function(err, state){
if(err){
console.log(err);
} else
{
res.render("edit", {state: state});
}
});
});
In the above code snippet, isLoggedIn is the middleware function to check for authentication. Its definition is given below:
// middleware function
function isLoggedIn(req, res, next){
if(req.isAuthenticated()){
return next();
}
res.redirect("/admin");
}
So, the question is, how to pass a parameter like a string, an integer or a path variable to the middleware function so that it can be used in the routing url ?
I had the same requirement and this approach works for me.
Middleware file validate.js
exports.grantAccess = function(action, resource){
return async (req, res, next) => {
try {
const permission = roles.can(req.user.role)[action](resource);
// Do something
next();
}
catch (error) {
next(error)
}
}
}
Use of middleware in route file. grantAccess('readAny', 'user')
router.get("/",grantAccess('readAny', 'user'), async (req,res)=>{
// Do something
});
Follow this approach, it might do the job for you
app.use(function(req, res, next){
console.log(req);
this.req = req;
// assign value like this
this.req.body.custom_data = ['zz', 'aaa', ....];
next();
});
app.get("/map/:symbol/edit", isLoggedIn, function(req, res){
State.findOne({symbol: req.params.symbol}, function(err, state){
if(err){
console.log(err);
} else {
res.render("edit", {state: state});
}
});
});
function isLoggedIn(req, res, next){
console.log(req.body);
if(req.isAuthenticated()){
return next();
}
res.redirect("/admin");
}
This is the way I'm using it, I take a little bit of Hardik Raval answer.
helpers.validateRole = (roles) => {
return async (req, res, next) => {
try {
const authHeader = req.headers['authorization']
const token = authHeader && authHeader.split(' ')[0]
if (token == null) return res.json({error:true, msg: "Unauthorized"})
const user = jwt.decode(token)
let isValid = false
roles.map((r,i)=>{
if (r === user.role){
isValid = true
}
})
if (isValid){
// User role is valid
next();
}else{
// User role is not valid
util.returnError("Unauthorized", res);
}
}
catch (error) {
next(error)
}
}
}
And I called like this.
router.get( "/requirements/list_of_requirements/:page/:rows", isAuthenticated, validateRole([6]), async (req, res) => {
//* All the logic
})
Last time I used passport authenticate for login in the following manner:
router.post('/login', parserUrlEncodedTrue, (request, res, next) => {
console.log('logging in user');
passport.authenticate('local', (err, user, info) => {
var options = "";
if(err){
return next(err);
}
if(!user){
return res.json({
error: true,
message: "User doesn't exist"
});
}
req.logIn(user, function(err) {
if (err){
return next(err);
}
return res.json({
error: false,
message: "Login Success"
});
});
})(req, res, next);
Now I'm trying to use formidable.js and it works fine for registration but can't make it work with login.
let form = new formidable.IncomingForm();
form.parse(req, (err, fields, files) => {
if(err){
helpers.errors(err, res);
} else {
passport.authenticate('local', (err, user, info) => {})(req, res, next);
}
});
passport.authenticate gets to req before it is parsed by formidable. I'm also using express and passport-local-mongoose.
I want to display the username in case the user is logged in (function 3). Initially, I only had function 1. I've changed function 1 into function 2 in order to display the username of the logged in user. I am not sure if this is the right way to do this, because I don't know if I need parameters res and next, and function next(). Any idea?
I am using NodeJS, ExpressJS and Passport
1.
function isLoggedIn(req, res, next) {
if (req.isAuthenticated()) {
return next()
} else {
res.redirect('/login')
}
}
2.
function isLoggedIn(req, res, next) {
if (req.isAuthenticated()) {
return true
}
return false
}
3.
router.get('/', function(req, res) {
if (isLoggedIn) {
res.render('index', {
username: req.user.username
})
} else {
res.render('index')
}
})
You are pretty much doing the right but calling the method isLoggedIn like a variable. I have corrected the same below.
2.
function isLoggedIn(req, res, next) {
if (req.isAuthenticated()) {
return true;
}
return false;
}
3.
router.get('/', function(req, res, next) {
if (isLoggedIn(req, res, next)) {
res.render('index', {
username: req.user.username
});
} else {
res.render('index');
}
});
Also, you can refactor the isLoggedIn method as below.
function isLoggedIn(req, res, next) {
return req.isAuthenticated();
}
I'm experimenting with Passportjs and the code for a Custom Callback is:
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'm happy with all of this code except for the second to last line (req, res, next); - Could someone explain why these parameters are added on the end. This is probably more of a JS question than a Passport question but any help is much appreciated.
The "javascript" answer is that it returns a function which is called again with those 2nd set of arguments.
That function is the "accumulator for failures from each strategy in the chain".
https://github.com/jaredhanson/passport/blob/master/lib/middleware/authenticate.js
You can rewrite it without the anonymous function, or the custom callback. Just use passport's passport.use(new LocalStrategy()) function to create the new strategy. See 'Configure' docs.
passport.use(new LocalStrategy(
function(username, password, done) {
logIn({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));
The only change you need to make is move the logIn function to be in this file, and not a method to req. Then you can simply call passport.authenticate like so:
app.get('/login', passport.authenticate('local', { successRedirect: '/',
failureRedirect: '/login' }));
So instead of using res.redirect in the callback, you just use passport's built in successRedirect and failureRedirect properties. You can see their docs as well, on the authentication page.
Connect/Express middleware function has signature:
function(req, res, next)
passport.authenticate() can be used as a middleware, e.g:
app.post('/login', passport.authenticate('local'), nextMiddleware);
This means authenticate() returns a middleware function object, which you can evoke with (req, res, next) parameters to continue the app's request-response cycle.
So I have multiple routes like this:
app.get('/user', function(req, res) {
model.find({}, function(err, docs) {
res.send(docs);
});
});
app.get('/user/:id', function(req, res) {
model.findById(req.params.id, function(err, doc) {
res.send(doc);
});
});
multiplied by a lot.
I'd like to know if they would be a way to factor out that callback function like that:
app.get('/user', function(req, res) {
model.find({}, sendInResponse);
});
app.get('/user/:id', function(req, res) {
model.findById(req.params.id, sendInResponse);
});
My problem is the res scope.
I would refactor it like this:
var find = function (queryFn) {
return function (req, res, next) {
var query = queryFn.apply({ req: req, res: res });
model.find(query, function (err, docs) {
res.send(docs);
});
}
};
Now you have generic query middleware which you can use like this:
app.get('/user/:id', find(function () { return { id: this.req.params.id } });
This is just the tip of what's possible and you can even refactor it more. This is a route from an app I created which uses only generic middleware:
app.get('/users/:username',
data.one('user', data_users.byUsername, 'params.username'),
data.many(data_grows.byUserId, 'locals.user.id'),
general.render('grows/index'));