Passport.js authentication middleware with Node.js - javascript

I'm trying to make secure routers by using jsonwebtoken on Node.js server.
And I'm using passport.js to authenticate user with JWT.
At first, I put all logics in controller.
But all secure routers need to check authentication, so I tried to divide the authenticate part as a middleware
Before
user.controller.js
/**
* GET /user
* Get user data
*/
exports.getUser = (req, res, next) => {
passport.authenticate("jwt", { session: false }, (err, payload, info) => {
if (err) return next(err);
if (!payload) return next(info);
User.findOne({ email: payload.email }, (err, user) => {
if (err) return next(err);
if (!user) return next("no matching user found");
res.status(200).send({ email: user.email });
});
})(req, res, next);
};
app.js
const userController = require('user.controller.js');
app.get('/user', userController.getUser);
After
passport.js
/**
* Check authentication
*/
exports.checkAuth = (req, res, next) => {
passport.authenticate("jwt", { session: false }, (err, payload, info) => {
if (err) return next(err);
if (!payload) return next(info);
req.user = payload;
next();
})(req, res, next);
};
user.controller.js
/**
* GET /user
* Get user data
*/
exports.getUser = (req, res, next) => {
User.findOne({ email: req.user.email }, (err, user) => {
if (err) return next(err);
if (!user) return next("no matching user found");
res.status(200).send({ email: user.email });
});
};
app.js
const passportConfig = require('passport.js');
const userController = require('user.controller.js');
app.get('/user', passportConfig.checkAuth, userController.getUser);
In original user.controller.js I could get email from payload.email.
BUT after I divided the original file, I cannot access the email value at user.controller.js.
So I searched some ways how to pass data from one middleware to another, and used req.user.
Question
Is this correct structure to authenticate with jwt, passport.js?
Is this correct way to pass data between middlewares? or is there any better way?

This is a good practice to use req to pass data from middleware to others.
By the way, you shouldn't call by yourself next() from passport custom callback (this is not a middleware). Passport will do next middleware call himself in case token is valid.
/**
* Check authentication
*/
exports.checkAuth = (req, res, next) => {
passport.authenticate("jwt", { session: false }, (err, payload, info) => {
if (err) return next(err);
if (!payload) return next(new Error('wrong to'));
//next()
})(req, res, next);
};
From your "Before" step, there is a reason to use a custom callback because you check user email existence from it.
But from your "After" step, the user check logic has moved into another middleware. So you can just use passport default middleware.
exports.checkAuth = passport.authenticate("jwt", { session: false });
And then user.controller.js will be called with the token data bind to req.user in case token is validated by Passport.
At this moment, you can proceed to email verification.

Controller function is executing before middleware check, update your middleware like below using Promisify doc
const util = require('util');
const authenticate = util.promisify(passport.authenticate);
exports.checkAuth = async (req, res, next) => {
// passport.authenticate("jwt", { session: false }, (err, payload, info) => {
// if (err) return next(err);
// if (!payload) return next(info);
// req.user = payload;
// next();
// })(req, res, next);
try {
const payload = await authenticate("jwt", { session: false });
req.user = payload;
//Do something
next();
} catch (error) {
}
};

Related

the correct way using middleware in express

hey I want to make sure if I use the correct way for middleware in my simple express app, I am trying to find the email unique for register
here is my example
const isUnique = (req, res, next) => {
User.findOne({
where:{
email: req.body.email
}
})
.then(getUser => {
if(getUser){
next("/userAlreadyExist") // router
// or can i render to to html? i am using ejs
} else {
next()
}
})
.catch(next())
}
app.post('/register', isUnique ,(req, res) => {
res.send(`thank you for register`)
}
I want to make sure the email already exists or no, so I want to pass it on middleware first, and get a page for isUnique, if the email already in use, I want to redirect it to next router called '/emailExist', and if it success i want to redirect it to router /success
can anyone help me if that code wrong or no? just want to make sure :D
You have a lot of options, here are a couple.
You can redirect users to specific pages based on whether or not the email exists. Within your /emailAlreadyExists and /registerSuccess routes you can render whatever templates you want or return some data.
const isUnique = (req, res, next) => {
User.findOne({
where:{
email: req.body.email
}
})
.then(getUser => {
if (getUser) {
res.redirect('/emailAlreadyExists');
} else {
res.redirect('/registerSuccess'); // or just call next()
}
})
.catch(next("DB error"));
}
Pass along the results of the db query and let your final middleware handle it:
const isUnique = (req, res, next) => {
User.findOne({
where:{
email: req.body.email
}
})
.then(getUser => {
req.user = getUser;
next();
})
.catch(next());
}
app.post('/register', isUnique ,(req, res) => {
if (req.user) {
res.send('User already exists');
} else {
res.send(`thank you for register`);
}
}
You can also create an error handling middleware:
const isUnique = (req, res, next) => {
User.findOne({
where:{
email: req.body.email
}
})
.then(getUser => {
if(getUser){
next("Error: user already exists"); // or some other error message/object
} else {
next(); // continue to next middleware
}
})
.catch(next("DB error")); // handle errors throw from DB read
}
app.post('/register', isUnique ,(req, res) => {
res.send(`thank you for register`)
}
/*
If you call "next" with an argument, Express will skip
straight to this error handler route with the argument
passed as the "err" parameter
*/
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send(`An error occurred: ${err}`);
})

How to send an object and authenticate with passport at the same time

As vague as the question seems, I need a way to send a json object and also authenticate with passport at the same time. The object is req.isAuthenticated which will be picked up with axios later in the frontend as a checkpoint. That's what I intend. So far with the code below, the object will not be sent.
app.get('/login',
passport.authenticate('saml', {
successRedirect: '/assert',
failureRedirect: '/',
}),
(req, res) => {
res.json({isAuthenticated: req.isAuthenticated()})
}
);
Here is example sample from my project:
authorizeLocal: (req, res, next) => {
passport.authenticate('local-auth', (err, user, info) => {
if (info) console.log(info);
if (err) return next(err);
if (!user) return res.status(200).send({failReason: 'wrong login/password'});
req.logIn(user, err => {
if (err) return next(err);
delete user.password;
req.session.cookie.maxAge = 24 * 60 * 60 * 1000; // 24 hours
if (user.role === 'operator') {
user.status = 'Online';
operatorsService.setStatus('Online', user.id)
.then(result => {
dialogsService.getWaitingDialogs();
user.work_time = result;
res.status(200).send(user);
})
.catch(() => res.status(200).send({failReason: 'Service error'}));
} else res.status(200).send(user);
});
})(req, res, next);
},
There you can see passport req.logIn, which (needs local-auth strategy or tother in your case) performs auth and if success fires callback logic. Deeper you can have any user/object get/generation logic. I left my case for example. OperatorsService.setStatus returns some time data, which is stored to user (user is got as callback param after strategy logic run) end sent as response. You can add user.isAuthenticated = req.isAuthenticated(); there.
So you'll have smth like:
auth.route.js
app.get('/login', authCtrl.authorizeLocal);
authCtrl.js
authorizeLocal: (req, res, next) => {
passport.authenticate('saml', (err, user, info) => {
if (info) console.log(info);
if (err) return next(err);
// if (!user) return res.status(200).send({failReason: 'wrong login/password'});
req.logIn(user, err => {
if (err) return next(err);
res.status(200).send({isAuthenticated: req.isAuthenticated()}));
});
})(req, res, next);
},

How to turn an export.signup and export.signin into a module.exports?

I'm developing a module which I'm using for passport authentication with ExpressJS, and I came up with this solution to gather all the passports methods I'm using:
// passport-controller-js
exports.signup = (passport) => (req, res, next) => {
// Authenticate methods ================
passport.authenticate('local-signup', function(err, user, info) {
if (err) {
return next(err); // will generate a 500 error
}
// Saving user...
return res.send({ success : true, message : 'signup succeeded' });
})(req, res, next);
};
exports.signin = (passport) => (req, res, next) => {
passport.authenticate('local-login', function(err, user, token, info) {
if (err) {
return next(err); // will generate a 500 error
}
req.login(user, loginErr => {
if (loginErr) {
return next(loginErr);
}
return res.send({ success : true, message : 'signin succeeded' });
});
})(req, res, next);
};
But since this module will increase adding more strategies I'm thinking if there is a way to put all of them inside a module.exports like:
module.exports = (passport) => {
function signin(req, res, next) {
passport.authenticate('local-login', function(err, user, token, info) {
if (err) {
return next(err); // will generate a 500 error
}
req.login(user, loginErr => {
if (loginErr) {
return next(loginErr);
}
return res.send({ success : true, message : 'signin succeeded' });
});
})(req, res, next);
};
I know that dosn't work Im just wondering if there is a possible solution like that so when I need to require those methods on my router file for example I can do this:
// auth.js (passport is passed from index.js)
const passportController = require('../controllers/passport-controller')(passport);
// Process the signup form
router.post('/signup', passportController.signup);
router.post('/signin', passportController.signin);
Instead of:
// auth.js (passport is passed from index.js)
const passportController = require('../controllers/passport-controller');
// Process the signup form
router.post('/signup', passportController.signup(passport));
router.post('/signin', passportController.signin(passport));
As you can see is just matter of looking for the most legible way to code and keep it simpler as possible.
Any help would be appreaciated, thank you very much.
Sounds like you want to make a module that exports a single function which returns an object. The syntax for that is
module.exports = passport => ({
signup(req, res, next) {
…
},
signin(req, res, next) {
…
}
});

How can i store token value into my local javascript file

Hi am a beginner to Nodejs i have used passportjs token based authentication if the user logins it provides a token for each user i want to perform some operations based for the users who has token values for example if the user want to see the list of registered users they can view it if he has the token value. Now it provides me the token value perfectly in Postman but i don't know how to store it in a variable and call it via FRONT-END. I want do it via Front End(If he clicks the get users button) it should display the list of users.I have done that in POSTMAN it works finely i don't have an idea how to do it via frontend.
My user Code(Login/Logout)
var express = require('express');
var router = express.Router();
var User = require('../models/user');
var passport = require('passport');
var Verify = require('./verify');
/* GET users listing. */
router.route('/')
.get(Verify.verifyOrdinaryUser, function(req, res, next) {
User.find({}, function (err, users) {
if (err) throw err;
res.json(users);
});
});
router.post('/register', function(req, res, next) {
User.register(new User({ username : req.body.username }),req.body.password, function(err, user) {
if (err) {
return res.status(500).json({err: err});
}
user.save(function(err,user) {
passport.authenticate('local')(req, res, function () {
return res.status(200).json({status: 'Registration Successful!'});
});
});
});
});
router.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) {
return next(err);
}
if (!user) {
return res.status(401).json({
err: info
});
}
req.logIn(user, function(err) {
if (err) {
return res.status(500).json({
err: 'Could not log in user'
});
}
var token = Verify.getToken(user);
res.status(200).json({
status: 'Login successful!',
success: true,
token: token
});
});
})(req,res,next);
});
router.get('/logout', function(req, res) {
req.logout();
res.status(200).json({
status: 'Bye!'
});
});
module.exports = router;
Main.js File. In this main.js file i want to send that token in this get method any idea?
$(".get-users-button").click(function() {
$.ajax({
method: "GET",
url: " http://localhost:3000/users"
})
.done(function(msg) {
console.log(msg);
template(msg);
});
});
When you get back a successful response from the POST to your /login endpoint, store the token on client-side (e.g., window.localStorage.setItem('<your-namespace>-user-token', <the token goes here>);)
Then, when user clicks the 'get-users-button', get the token out of storage (e.g., window.localStorage.getItem('<your-namespace>-user-token'); and store it in a variable if you want.
Then, on your request to get users, add your { 'x-access-token': <token variable goes here> } to your request headers.
As per the documentation for Passport:
If authentication succeeds, the next handler will be invoked and the req.user property will be set to the authenticated user.
Now if I'm understanding your question correctly, you want to pass the token value you obtain from:
var token = Verify.getToken(user)
to the view in which your front-end can do something with. You can pass variables to the view using the following middleware:
app.use((req, res, next) => {
res.locals.token = Verify.getToken(req.user)
next()
}
See the documentation for res.locals for more details.
Example usage:
app.js
const express = require('express')
const app = express()
app.set('view engine', 'pug')
app.use((req, res, next) => {
res.locals.text = 'asdf'
res.locals.token = 'abc'
next()
})
app.get('/', (req, res) => {
res.render('index')
})
app.listen(3000, () => {
console.log('listening on 3000')
})
views/index.pug
doctype html
html
head
title= title
body
h1= text
script.
console.log('#{token}')

create a custom passport-jwt strategy middleware callback

I want to create a custom middleware for passport-jwt to handle authentication.
here is what I have done to create my own middleware :
var models = require('../models');
var passport = require("passport");
var passportJWT = require("passport-jwt");
var config = require("../config/config.json");
var ExtractJwt = passportJWT.ExtractJwt;
var Strategy = passportJWT.Strategy;
var params = {
secretOrKey: config.jwtSecret,
jwtFromRequest: ExtractJwt.fromAuthHeader()
};
/**
* jwt authentication strategy
*/
var strategy = new Strategy(params, function(payload, done) {
models.User.findById(payload.id)
.then((user)=>{
if (user) {
return done(null, {
id: user.id,
username : user.username
});
} else {
return done(new Error("User not found"), false);
}
}).catch((err)=>{
return done(err, false);
});
});
passport.use(strategy);
module.exports = {
initialize: function() {
return passport.initialize();
},
authenticate: (req, res, next)=>{
passport.authenticate('jwt', { session: false }, (err, user, info)=>{
if (err) { return next(err); }
if (!user) { return res.send("Custom Unauthorised").end(); }
// edit as per comment
//return res.send("Test Route Accessed").end();
req.user = user; // Forward user information to the next middleware
next();
})(req, res, next);
}
};
but everytime I type 'npm start' to run the app I face this error :
if (request.headers[AUTH_HEADER]) {
^
TypeError: Cannot read property 'headers' of undefined.
the authorization header is set in the request.
yes I did Find the answer here it is :
first define the strategy logic:
var strategy = new Strategy(params, function (payload, done) {
//finding the user in the database
console.log(payload);
models.users.findById(parseInt(payload.userId))
.then((user) => {
//if the user is found
if (user) {
return done(null, {
id: user.id,
username: user.username
});
} else {
return done(new Error("User not found"), null);
}
}).catch((err) => {
console.log(err);
return done(new Error("uncaught error! try again later"), null);
})
});
then make passport use that strategy"
passport.use(strategy);
and finally export the initialization function and the middleware function
module.exports = {
initialize: function () {
return passport.initialize();
},
authenticate: function (req, res, next) {
return passport.authenticate("jwt", {
session: false
}, (err, user, info) => {
if (err) {
console.log(err);
return next(err);
}
if (!user) {
return res.json({
status: 'error',
error: 'ANOTHORIZED_USER'
});
}
// Forward user information to the next middleware
req.user = user;
next();
})(req, res, next);
}
};
and then you can call the function authenticate defined above as a middleware in your routes.
here is an example :
//import the express router
var express = require('express');
var router = express.Router();
//here I am importing the functions defined above, I put them in the config folder
var jwt_login_strategy = require('../config/jwt-login-strategy');
//and finally use the jwt_login_strategy as a middleware
router.post('something', jwt_login_strategy.authenticate, your_other_middleware(req, res, next)=>{...});
you have to call the authenticate function without adding parentheses, just like this jwt_login_strategy.authenticate.
hope it will solve your problem as it did for mine.

Categories