I have a Node.js Express API that doesn't have any security and now I'm looking to patch on JWT-based authorization.
I have it working (using the code below) for one method. Is there an easy way to apply this to all the methods? e.g., maybe using app.all? Or do I have to add the code to every method?
app.get('/people/:id', ensureToken, (req, res, next) => {
var id = req.params.id;
getPerson(id, (err, people) => {
if (err) {
next(err);
return;
}
jwt.verify(req.token, process.env.JWT_KEY, function(err, data) {
if (err) {
res.sendStatus(403);
} else {
res
.status(200)
.set('Content-Type', 'application/json')
.set("Connection", "close")
.send(person);
}
});
});
});
function ensureToken(req, res, next) {
const bearerHeader = req.headers["authorization"];
if (typeof bearerHeader !== 'undefined') {
const bearer = bearerHeader.split(" ");
const bearerToken = bearer[1];
req.token = bearerToken;
next();
} else {
res.sendStatus(403);
}
}
You can do as:
app.use(function(req, res, next){
const bearerHeader = req.headers["authorization"];
if (typeof bearerHeader !== 'undefined') {
const bearer = bearerHeader.split(" ");
const bearerToken = bearer[1];
req.token = bearerToken;
next();
} else {
res.sendStatus(403);
}
});
I'd recommend checking out passport-jwt
You can use this as middleware as well:
app.get('/people/:id', passport.authenticate('jwt', { session: false }), (req, res, next) => { });
The advantage to this over using app.use is that you can specify which routes should be authenticated. i.e., you can exclude a login or registration route. without having to hack in checks like if (req.path == '/login')The other advantage is that using passport you can later add additional authentication methods, should you choose. There's also quite a bit of community support.
I implemented it with app.all, excluding the login route from the check:
app.all(process.env.API_BASE + "*", (req, res, next) => {
if (req.path.includes(process.env.API_BASE + "login")) return next();
return auth.authenticate((err, user, info) => {
if (err) { return next(err); }
if (!user) {
if (info.name === "TokenExpiredError") {
return res.status(401).json({ message: "Your token has expired. Please generate a new one" });
} else {
return res.status(401).json({ message: info.message });
}
}
app.set("user", user);
return next();
})(req, res, next);
});
The full solution here: https://jonathas.com/token-based-authentication-in-nodejs-with-passport-jwt-and-bcrypt/
Related
I have a login system with tokens. When logging in, it checks whether such a user exists, doesn't have information about current user during the login session. What's the easiest way to check it and send response to frontend?
Routes:
function verifyToken(req, res, next) {
if (!req.headers.authorization) {
return res.status(401).send('Unauthorized request');
}
let token = req.headers.authorization.split(' ')[1];
if (token === 'null') {
return res.status(401).send('Unauthorized request');
}
let payload = jwt.verify(token, 'secretKey');
if (!payload) {
return res.status(401).send('Unauthorized request');
}
req.userId = payload.subject;
next();
}
router.post('/register', (req, res) => {
let userData = req.body;
let user = new User(userData);
user.save((error, registeredUser) => {
if (error) {
console.log(error);
} else {
let payload = { subject: registeredUser._id };
let token = jwt.sign(payload, 'secretKey');
res.status(200).send({ token });
}
})
})
router.post('/login', (req, res) => {
let userData = req.body;
User.findOne({ email: userData.email }, (error, user) => {
if (error) {
console.log(error);
} else {
if (!user) {
res.status(401).send('Invalid email');
} else
if (user.password !== userData.password) {
res.status(401).send('Invalid password')
} else {
let payload = { subject: user._id };
let token = jwt.sign(payload, 'secretKey');
res.status(200).send({ token });
}
}
})
})
You could try using a middleware to retrieve the token from the Authorization header and retrieve the userId from there, the middleware could look something like this:
const decodeToken = (token) => {
return jwt.verify(token, 'secretKey', (err, decoded) => {
if (err) {
return undefined;
}
return decoded;
});
};
const authorize = (req, res, next) => {
if (!req.headers.authorization) {
return res.status(401).send({message: 'UNAUTHORIZED'});
}
const token = req.headers.authorization.split(' ')[1];
if (!token) {
return res.status(401).send({message: 'UNAUTHORIZED'});
}
const decodedToken = decodeToken(token);
if (!decodedToken) {
return res.status(401).send({message: 'UNAUTHORIZED'});
}
req.userId = decodedToken.subject;
next();
};
module.exports = authorize;
Hope this helps you, if not, I hope you find your answer :)
EDIT
To use the middleware you only need to add it to your route, I´ll leave you an example with a get request:
const authorize = require('../middleware/authorize');
router.get('/someroute', authorize, (req, res) => {
// authorize will verify the token and attach the userId to your request
// so to use it you'll only need to call req.userId
// something like this
console.log('current logged user id', req.userId);
// YOUR CODE HERE
});
I am developing an app, where I invite other people. So I just wanted to skip passport auth if the url pattern matches. Here is code:
const routes = (app) => {
app.all('/*', (req, res, next) => {
if ((req.path === '/api/v2/auth/sign_up') || (req.path === '/api/v2/members/invite*'))
next();
else if (req.path === '/api/v2/auth/sign_in') {
passport.authenticate('local', { session: false }, (err, user, info) => {
if (!user || err || info != undefined) {
return res.status(500).send({ message: "Something went wrong, Please try again!" });
}
req.user = user;
next();
})(req, res, next);
} else {
passport.authenticate('jwt', { session: false }, (err, user, info) => {
if (!user || err || info != undefined) {
return res.status(500).send({ message: "Something went wrong, Please try again!" });
}
req.user = user;
next();
})(req, res, next);
}
app.use('/api/v2/members/invite',
require("./memberInviteRoute"));
app.use('/api/v2/members',
authorize('admin', 'company'),
require("./memberRoute"));
});
}
module.exports = routes;
i've got a problem
I'm trying to make a simple login page,
but i've problem with passing the token through http header
app.post('/login',(req,res) => {
var body = req.body.user;
User.findByCredentials(body.email,body.password).then((user) => {
return user.generateAuthToken().then((token) => {
res.header('x-auth', token).send(user);
});
}).catch((e) => {
res.status(400).send();
});
});
here is the route for login page, I saved the token in 'x-auth' in header, and it's work
but...
var authenticate = (req, res, next) => {
var token = req.header('x-auth');
User.findByToken(token).then((user) => {
if (!user) {
return Promise.reject();
}
req.user = user;
req.token = token;
next();
}).catch((e) => {
res.status(401).send();
});
};
module.exports = {authenticate};
this function is middle-ware for privet routes, when I asking for 'x-auth' i've got 'undifined'
here is the piece that connect between both codes
app.get('/',authenticate,(req,res) => {
res.sendFile(publicPath + '/index.html');
});
someone can help me with that?
I want that I can access a router with or without a token. If user has a token, than give me req.user
Like this:
router.get('/profile', function(req, res) {
if(req.user) { // or if (req.isAuthenticated())
res.send('logged')
} else {
res.send('not loggedIn')
}
});
My app:
var JwtStrategy = require('passport-jwt').Strategy,
ExtractJwt = require('passport-jwt').ExtractJwt;
var opts = {}
opts.jwtFromRequest = ExtractJwt.fromAuthHeader();
opts.secretOrKey = 'sh';
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findOne({id: jwt_payload.sub}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false);
// or you could create a new account
}
});
}));
If I try to access /profile without a token, works fine.
But, when a try to access /profile with a token in header give me not loggedIn
I want to access it even without a token and, if I provided a toke, give me the user.
ps: Already tested using passport.authenticate('jwt') in route and works. If I give token let me access, if not give me unauthorized.
This is how I'm doing it:
const passport = require('passport');
const optionalJwt = function (req, res, next) {
if (req.headers['authorization']) {
return passport.authenticate('jwt', { session: false })(req, res, next);
}
return next();
};
app.get('/my/route', optionalJwt, function (req, res, next) {
if (req.isAuthenticated()) {
res.send('My name is ' + req.user.name);
} else {
res.send('I\'m not authenticated :(');
}
});
It will still fail if the auth header is included but the token has expired or it's invalid, but it might serve the purpose. For me it did.
Change you router as follows
router.get('/profile', authenticateUser(), profile());
function authenticateUser(req, res, next) {
// your strategy here...
if (authenticated) {
req.user = user;
next();
} else {
return res.status(401).send("Not authenticated");
}
}
function profile(req, res, next) {
var userId = req.user.id;
User.findById(userId, function(err, user) {
if (err) { return res.status(500).json(err); }
return res.json(user);
})
}
you should be using one of the below to access request data
if (req.params.user) {do something...}
or
if (req.body.user) {do something...}
the request object has no user attribute.
I'm new to Express 4 and i'm wondering something about how to implement this thing: I'm using jwt to authenticate the consumer of my API, to do that i have a pretty simple middleware to check the validity of the jwt token:
var requireValidToken = function(req, res, next) {
var token = req.body.token || req.query.token || req.headers['x-access-token'];
if (token) {
try {
var decoded = jwt.verify(token, req.app.get('superSecret'));
} catch(err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
}
req.user = decoded.user;
next();
} else {
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
};
This is working pretty well, but now, i want to extend this to be able to check the role of the user:
router.get('/anAdminRoute', requireRole('ROLE_ADMIN'), function (req, res, next) {
// some code...
});
So i added a middleware for this:
var requireRole = function(role) {
return function(req, res, next){
// Dummy tests...
if(req.user.role == roles.admin || req.user.role == role){
next();
} else {
return res.status(403)({
success: false,
message: "Token valid, but you don't have the right permission to access this resource :)"
});
}
}
}
But as this requireRole() function while obviously checks for a valid jwt token, i'm wondering how can i call my requireValidToken middleware within this function, and so not having to explicitly call it for each route i want to protect.
An easy solution would have been not to use requireValidToken as a middleware but i still want to be able to use it to protect certain routes
Edit: Solution
Chaining middlewares is a simple as that:
var middleware2 = function(param) {
return function(req, res, next){
middleware1(req, res, function(){
// middleware2 code
});
}
}
If anybody interested, my final working solution to validate a user role:
var jwt = require('jsonwebtoken'),
roles = require('../models/user').roles;
// authentication middleware
// check if the given jwt token is valid
var requireValidToken = function(req, res, next) {
var token = req.body.token || req.query.token || req.headers['x-access-token'];
if (token) {
try {
var decoded = jwt.verify(token, req.app.get('superSecret'));
} catch(err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
}
req.user = decoded.user;
next();
} else {
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
};
var requireRole = function(role) {
return function(req, res, next){
requireValidToken(req, res, function(){
if(req.user.role == roles.admin || req.user.role == role){
next();
} else {
return res.status(403).send({
success: false,
message: "Token valid, but you don't have the right permission to access this resource :)"
});
}
});
}
}
module.exports = {
requireValidToken: requireValidToken,
requireRole: requireRole
}
Completely misread your question. If you want to call requireValidToken for certain situations, you can pass along the req and res objects to the middleware function, along with an anonymous callback. How you get the middleware function largely depends on your application architecture so I'll assume I have the requireValidToken within my context:
var requireRole = function(role) {
return function(req, res, next){
// Dummy tests...
requireValidToken(req, res, function () {
if(req.user.role == roles.admin || req.user.role == role){
next();
} else {
return res.status(403)({
success: false,
message: "Token valid, but you don't have the right permission to access this resource :)"
});
}
});
}
};
This syntax worked for me. Where auth is another middleware module
import auth from "../middlewares/auth.js";
export default function (req, res, next) {
auth(req, res, function () {
if (req.params.id !== req.user._id)
return res
.status(401)
.send({ status: "error", message: "User does not have rights to modify this data." });
next();
});
}