How can i pass data from middleware to router? - javascript

We have authenticateToken middleware in getpost router. The authenticateToken middleware validates the token.
If the token has expired, the if(err) conditional statement is executed. At this time, the accessToken is issued again through the refreshtoken.
I would like to pass this issued accessToken to the router. console.log(accessToken); How do I fix the code to do this?
this is my code
(index.js)
router.post('/getpost', authenticateToken, async (req, res, next) => {
try {
console.log(accessToken);
} catch (error) {
console.error(error);
next(error); // status 500
}
});
(middleware.js)
exports.authenticateToken = (req, res, next) => {
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, async (err, user) => {
if (err) {
jwt.verify(
refreshToken,
process.env.REFRESH_TOKEN_SECRET,
async (err, user) => {
if (err) return res.sendStatus(403);
const accessToken = await generateAccessToken(users); // i want to pass this accessToken to router
console.log('accessToken::::', accessToken);
},
);
res.json({accessToken: accessToken});
}
req.user = user;
next();
});
};
function generateAccessToken(user) {
return jwt.sign(user, process.env.ACCESS_TOKEN_SECRET, {expiresIn: '55s'});
}
app.post("/token", (req, res) => {
const refreshToken = req.body.token;
console.log("refreshToken:", refreshToken);
if (refreshToken == null) return res.sendStatus(401);
if (!refreshTokens.includes(refreshToken)) return res.sendStatus(403);
jwt.verify(refreshToken, process.env.REFRESH_TOKEN_SECRET, (err, user) => {
if (err) return res.sendStatus(403);
const accessToken = generateAccessToken({ name: user.name });
res.json({ accessToken: accessToken });
});
});

I think you can do something like this
exports.authenticateToken = (req, res, next) => {
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, async (err, user) => {
if (err) {
jwt.verify(
refreshToken,
process.env.REFRESH_TOKEN_SECRET,
async (err, user) => {
if (err) return res.sendStatus(403);
const accessToken = await generateAccessToken(users); // i want to pass this accessToken to router
// Adding token to the req object
req.accessToken = accessToken
// Added this
next()
},
);
res.json({accessToken: accessToken});
} else {
req.user = user;
next();
}
});
};
function generateAccessToken(user) {
return jwt.sign(user, process.env.ACCESS_TOKEN_SECRET, {expiresIn: '55s'});
}
You pass token using req object, and get the token in route like this
router.post('/getpost', authenticateToken, async (req, res, next) => {
try {
// Access the token through req object
console.log(req.accessToken);
} catch (error) {
console.error(error);
next(error); // status 500
}
});
Edit: Wrote example based on code in question

Related

Passport.js authentication middleware with Node.js

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) {
}
};

function with variable returns for routes

I have multiple routes which need the same userdata from the database. I have a function to check if the user is loggend in but that function dont return the user variables.
route:
app.get('/template', isAuthenticated, function (req, res) {
MongoClient.connect(url, { useNewUrlParser: true, useUnifiedTopology: true }, (err, client) => {
if (err) throw err;
const db = client.db(dbname);
let collection = db.collection('users');
// find data in db
collection.findOne({ _id: userid }).then(user => {
if (user != null) {
res.render('template', { layout: 'temaplte', csrfToken: req.csrfToken(), username: user.username, avatar: user.picture });
} else {
console.log("No user with this id!")
}
}).catch((err) => { console.log(err);
}).finally(() => { client.close(); });
});
});
Is there a way to get the variables users from the db from a function like isAuthenticated? Do I need to write the findOne-function on every route?
Best way to reuse logic in routes is to refactor that functionality into its own middleware.
function loadUserData(req, res, next) {
MongoClient.connect(url, {
useNewUrlParser: true,
useUnifiedTopology: true
}, (err, client) => {
if (err) {
return next(err)
};
const db = client.db(dbname);
let collection = db.collection('users');
// find data in db
collection.findOne({
_id: userid
}).then(user => {
if (user != null) {
req.user = user; // augment the request object with user data (check res.locals docs too)
return next(); // pass control to next middleware
} else {
res.end("No user with this id!");
}
}).catch((err) => {
return next(err);
})
.finally(() => {
client.close();
});
});
}
app.get('/template', isAuthenticated, loadUserData, function(req, res) {
const user = req.user; // loadUserData populated req.user;
res.render('template', {
layout: 'temaplte',
csrfToken: req.csrfToken(),
username: user.username,
avatar: user.picture
});
});

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');
})

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);
},

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