I am trying to implement an authentication system for the express REST API.
I got this code for using jsonwebtoken from a tutorial.
My auth middleware verifyToken is not working. Please help.
var express = require('express');
var router = express.Router();
var jwt = require('jsonwebtoken');
var models = require('../models');
function verifyToken(req, res, next) {
var bearerHeader = req.headers['authorization'];
if (typeof bearerHeader !== undefined) {
} else {
res.sendStatus(403)
}
}
router.post('/tryjwt', verifyToken, (req, res, next) => {
res.send('It worked');
});
router.get('/login', function (req, res, next) {
const user = {
id: 1,
usename: 'ayoob',
email: 'ayoob#gmail.com'
}
jwt.sign({ user: user }, 'secretkey', (err, token) => {
res.json({ token: token })
});
});
module.exports = router;
you did not perform any action when if (typeof bearerHeader !== undefined) is true.
Make sure next is called so that the current middleware can pass control to the next middleware
put this code snippet inside the if statement
try{
const decode = jwt.verify(bearerHeader, 'secretkey')
//do something with the decode object
next()
}catch(err){
res.sendStatus(403)
}
I think that the problem is the async nature of the jwt.sign() function - it continues to sign the token while it continues with your code - returning an empty json. You need to wait for the jwt.sign() to finish before the res.json({token}), or do something while the magic of the jwt is happening.
Hope I helped :)
p.s. It's better practice to use promises, so your code will be cleaner and you won't got into callback hell, and for better error handling.
Related
I tried using Async-Await in NodeJs RESTful API but I seem to get an error I cannot resolve.
Here is my db.js:
const User = require('../models/User'),
user = {};
user.findUserByUsername = async function (username) => {
try {
const user = await User.findOne({username});
if (user)
return {data: user, status: 200};
return {message: `Cannot find user with username: ${username}`, status: 404};
} catch (err) {
return err;
}
};
module.exports = user;
And here is my api.js:
const express = require('express'),
router = express.Router(),
user = require('../db/db');
router.get('/user', async (req, res, next) => {
const user = await user.findUserByUsername(req.query.username);
// ^
// Cannot access 'user' before initialization
if (!user.status)
return next(user);
res.status(user.status);
res.doc = user.status === 404 ? user.message : user.data;
next();
});
module.exports = router;
When making a HTTP request, my server is crashing at that point. Where is my mistake?
You're mixing your variable names up. You have user as an import, but you also have user as a variable you're trying to assign to as a result of findUserByUsername.
Use different variable names, and follow the capitalization convention for this sort of database lookup:
const express = require('express'),
router = express.Router(),
User = require('../db/db');
router.get('/user', async (req, res, next) => {
const user = await User.findUserByUsername(req.query.username);
I have been working on this problem for several days but I couldn't find any solution.
For each of my function in the route class I have this 2 Middlewares
index.js
const browser = require('browser-detect');
const detectBrowser = (req,res,next) => {
const result = browser(req.headers['user-agent']);
console.log(result);
if (result) {
if (result.name === 'chrome') {
console.log('Browser: '+result.name+ ' OK!');
next();
} else if (result.name=== 'firefox') {
console.log('Browser: '+result.name+ ' OK!');
next();
} else {
res.render('browser-response');
}
}
};
const redirectHome = (req,res,next) => {
if(req.session.token && req.session.firstName && req.session.lastName) {
if (!req.session.checked) {
res.redirect('/term-of-service');
}
else {
res.redirect('/index');
}
} else {
next();
}
};
and a simple function I want to test is:
router.get('/', detectBrowser, redirectHome, (req, res) => {
res.render("login");
});
I plan to use Mocha and Supertest as frameworks to test the router.get(/) function, but couldn't find anyway to mock the browser and the variables of session (session.token, session.firstName, session.lastName, session.checked).
Does anyone have an idea about this?
Thank you in advance.
For this question I already found out a work around solution.
For the middleware detectBrowser we just need to add
} else if (result.name=== 'node') { next(); }
because we are working in the node enviroment so those lines of code will make the test file pass through the middleware.
For the second middleware we just need to provide stubbing session information for the test. Below is an example :
const route = require ("../../routes/index");
const request = require("supertest");
const express = require("express");
const bodyParser = require('body-parser');
const chai = require('chai');
const expect = chai.expect;
//create app with valid credentials (not yet accept term of service )
let mockingLoggedIn;
//setup mocking session
mockingLoggedIn = express();
mockingLoggedIn.use(express.urlencoded({extended: false}));
mockingLoggedIn.set('view engine', 'ejs');
mockingLoggedIn.use(bodyParser.json());
mockingLoggedIn.use(bodyParser.urlencoded({extended: true}));
// lets stub session middleware
mockingLoggedIn.use(function (req, res, next) {
req.session = {};
next();
});
//provide a fake login
mockingLoggedIn.get('*', function (req, res, next) {
// and provide some accessToken value
req.session.token = "test";
req.session.firstName= 'test';
req.session.lastName = 'test';
req.session.checked = false;
next()
});
mockingLoggedIn.use(route);
And in the test we just need to use that stubbed app to make request to the route. For example:
describe("Testing / route and /login route works", () => {
it('testing index route: GET / should return status 200 and render login site',
(done)=> {
request(mockingLoggedIn) // request an app with stubbed credentials
.get("/")
.expect("Content-Type", "text/html; charset=utf-8")
.expect(200)
.end(function (err,res) {
expect(err).to.be.null;
done();
})
});
});
Hopefully this will help someone :)
I use node.js, passport and jwt bearer token to protect my routes. What I don't have yet is rate limiting and blocking of ip/user if too many false attempts. What's the best way to implement it for my setup?
I want to give it a try with rate-limiter-flexible. But how can I integrate e.g. the Login Example from here: https://github.com/animir/node-rate-limiter-flexible/wiki/Overall-example#login-endpoint-protection in my setup below?
helpers/rateLimiter.js
const express = require('express');
const redis = require('redis');
const { RateLimiterRedis } = require('rate-limiter-flexible');
/* What goes here? Example https://github.com/animir/node-rate-limiter-flexible/wiki/Overall-example#login-endpoint-protection doesn't seem to apply */
Those are my routes:
routes/index.js
const express = require('express');
const router = require('express-promise-router')();
const passport = require('passport');
const passLogin = passport.authenticate('local-login', { session: false, failWithError: true });
const { rateLimiter } = require('../helpers/rateLimiter');
...
router.route('/v1/login')
.post( rateLimiter, passLogin, function(err, req, res, next) {
return res.status(401).send({ status: 401, success: false })
}, controller.login );
router.route('/v1/abc/search')
.post( passJWT_ABC, function(err, req, res, next) {
return res.status(401).send({ status: 401, success: false })
}, controller.search );
You should export middleware in this case.
const express = require('express');
const redis = require('redis');
const { RateLimiterRedis } = require('rate-limiter-flexible');
async function loginRoute(req, res) {
// code from example https://github.com/animir/node-rate-limiter-flexible/wiki/Overall-example#login-endpoint-protection
}
export default async (req, res, next) => {
try {
await loginRoute(req, res);
next();
} catch (err) {
res.status(500).end();
}
}
And then you should take care of how authorise(), user.isLoggedIn and user.exists checks work with your application login approach.
There is an example with passport-local, should be useful for you as well https://github.com/passport/express-4.x-local-example/blob/67e0f735fc6d2088d7aa9b8c4eb25bc0052653ec/server-secure.js
i come to you because i'm stuck for rewrite my url in javascript.
For example when user make a request for deposit page , my url looks like '/member/deposit' but i just want '/deposit'. i'm using a MVC architecture.
index.js // Where i define all routes file
const
homeRoute = require('./home'),
memberRoute = require('./member');
function init(server) {
server.get('*', function (req, res, next) {
res.locals.user = req.user || null;
return next();
});
server.use('/', homeRoute);
server.use('/member', memberRoute);
}
module.exports = {
init: init
};
router member.js // Where i define all member routes
const
express = require('express'),
memberController = require('../controllers/member');
let router = express.Router();
router.get('/*', function(req, res, next){
if (req.session.user && (req.session.user.role == "member"))
next();
else
res.redirect('/login');
});
router.get('/', memberController.deposit);
router.get('/deposit', memberController.deposit);
router.get('/withdraw', memberController.withdraw);
module.exports = router;
controller member.js // where all code is executed before rendering
function deposit(req,res){
res.render('member/deposit',{
title:'Deposit'
});
}
function withdraw(req,res){
res.render('member/withdraw',{
title:'Withdraw'
});
}
Any help would be appreciated, i've not find response on stackoverflow
It can be use this way:
server.use('/', homeRoute);
server.use('/', memberRoute);
As long as the link in homeRoute and memberRoute have no duplication.
Or
The complete rewrite of url can be done as in the following link:
https://gist.github.com/ramonfritsch/06893c1c561d670687a9aee3bbc4e9c7
I´m facing a confusion issue while implementing the authentication for my restful api using passport local strategy.
Note:
I got the authentication working successfully when I´m doing it all in my index.js. But I want to use in Classes for better Code separation.
I have a passport.js Module
// config/passport.js
// load all the things we need
var LocalStrategy = require('passport-local').Strategy;
// load up the user model
var mysql = require('mysql');
var dbconfig = require('./database');
var connection = mysql.createConnection(dbconfig.connection);
module.exports = function(passport) {
// passport needs ability to serialize and unserialize users out of session
passport.serializeUser(function (user, done) {
//console.log("SER");
console.log(user),
done(null, user);
});
passport.deserializeUser(function (user, done) {
console.log("XXXX");
console.log(user);
connection.query("SELECT * FROM users WHERE name = ? ",user.name, function(err, rows){
console.log("DER");
console.log(rows);
done(err, rows[0]);
});
});
// passport local strategy for local-login, local refers to this app
passport.use('local-login', new LocalStrategy(
function (username, password, done) {
console.log("hhh");
console.log(username);
connection.query("SELECT * FROM users WHERE name = ? ",username, function(err, rows){
console.log(rows);
return done(err, rows[0]);
});
})
);
// route middleware to ensure user is logged in
function isLoggedIn(req, res, next) {
if (req.isAuthenticated())
return next();
res.sendStatus(401);
}
};
This is my Controller Class:
class AuthenticateController {
constructor(router, passport) {
this.router = router;
this.registerRoutes();
this.passport = passport;
}
registerRoutes() {
this.router.post('/login/:username/:password', this.login.bind(this));
//this.router.get('/logout', this.logout.bind(this));
this.router.get('/content', this.content.bind(this));
}
login(req, res) {
this.passport.authenticate("local-login", { failureRedirect: "/login"}),
res.redirect("/content");
}
content(req, res ) {
console.log(req.user);
if (req.isAuthenticated()) {
res.send("Congratulations! you've successfully logged in.")
} else {
res.sendStatus(401);
}
}
isLoggedIn(req, res, next) {
console.log(req.user);
if (req.isAuthenticated())
return next();
res.sendStatus(401);
}
}
module.exports = AuthenticateController;
The Controller gets the router and passport fully configured as parameters from my index.js.
//index.js
var express = require('express')
, cors = require('cors')
, app = express()
, passport = require('passport')
, morgan = require('morgan');
require('./config/passport')(passport); // pass passport for configuration
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(require('express-session')({secret: 'vidyapathaisalwaysrunning',
resave: true,
saveUninitialized: true }));
app.use(passport.initialize());
app.use(passport.session());
app.use(cors());
var apiRouter = express.Router();
app.use('/api', apiRouter);
//
var apiV1 = express.Router();
apiRouter.use('/v1', apiV1);
var authenticateApiV1 = express.Router();
apiV1.use('/auth', authenticateApiV1);
var AuthenticateController = require('./controllers/authenticate');
var ac = new AuthenticateController(authenticateApiV1, passport); //pass in our fully configured passport
//If I call this /login instead of the /auth/login/ in the Controller Class it works!
//app.post("/login",
// passport.authenticate("local-login", { failureRedirect: "/login"}),
// function (req, res) {
// res.redirect("/content");
// });
What is working and what is not working
The Authentication in general is working. In my posted index.js you see app.post("/login", .... If I call this one the authentication is successfully and if I try to reach the restricted content in /auth/content/ req.user has a value (the user object) and I can successfully call req.isAuthenticated() .
BUT, If I use the authentication from /auth/login/username/password the req.user is undefined when trying to reach the restricted Content.
I get no error and the response of /auth/login/username/password/ HTTP Code 301 - 'redirecting to /content.
I have currently no idea what I´m doing wrong here and I´m pretty new to the topic of Node/express/ passport ..
Hope someone has an Idea. If you need something else to help me, just mention it in the comments and I will do my best to provide you everything you need.
Thanks
EDIT:
I recently tried to read the req.user in the login function and even there it is undefined
login(req, res) {
this.passport.authenticate("local-login", { failureRedirect: "/login"}),
console.log(req.user) //undefined
res.redirect("/content");
}
I guess it could be some async problem and I should use some callback functions, but I don´t know how to apply this in my login()
EDIT 2:
Another Issue I´m facing is the integration of the isLoggedIn() request.
If I do this:
registerRoutes() {
this.router.get('/', this.isLoggedIn, this.getUsers.bind(this));
this.router.get('/:id', this.getSingleUser.bind(this));
}
it results in 401 - Unauthorized
A console.log(req.user); in the isLoggedIn() results in undefined.
But if I call the first route without calling isLoggedIn() and do console.log(req.user); the user object exists.
The correct use of callback with passport authentication for local strategy can be as below:
function(req, res, next){
passport.authenticate('local-login', function(err, user, info){
if(err)
return logger.log('error', err);
if(user)
req.login(user, function(err){
if(err) return next(err);
return res.json({'success': true});
});
if(!user)
return res.json({'error':true, 'message': info.message, 'type': info.type});
})(req, res, next);
}
Please note the use of req.login() to explicitly set user in session.
The only thing I'm finding strange is that your route declarations are different.
In the AuthenticateController the route is declared as:
this.router.post('/login/:username/:password', ...
While in index.js, the route is simply declared as
app.post("/login", ...
How is your client submitting the login credentials to the server? If it is by form, like the tutorial, could it be that having :username and :password declared as route params but being sent by form messes with passport?
Try registering the route exactly like index.js
this.router.post('/login', ...
EDIT:
I've found another dicrepancy. In AuthenticateController the res.redirect("/content"); is not wrapped inside a callback. So it is being executed before Authenticate finishes running.
In the index.js example, passport is being used as a route middleware:
app.post("/login",
passport.authenticate("local-login", { failureRedirect: "/login"}),
function (req, res) {
res.redirect("/content");
});
While in the passport.js it is inside the callback. Consider declaring it in the route:
registerRoutes() {
this.router.post('/login', this.passport.authenticate("local-login", { failureRedirect: "/login"}), this.login.bind(this));
(...)
}
login(req, res) {
res.redirect("/content");
}
O, better yet, why not use passport's option to declare both success and failure redirects, since that seems to be all that you are doing:
login(req, res) {
this.passport.authenticate("local-login", { successRedirect: "/content", failureRedirect: "/login" });
}
You are passing this.login.bind(this) as a middleware to this.router.post('/login/:username/:password', this.login.bind(this)); but login(req, res) only responds to the request with res.redirect("/content"); i.e. redirecting to /content
So like you said, you need to supply a callback that does something with the user that is returned from passports middleware verify callback.
app.post("/login",
passport.authenticate("local-login", { failureRedirect: "/login"}),
function (req, res) {
console.log(req.user); // log user in console
res.json({user: req.user}); // send user as json response
});
The custom callback mentioned by #divsingh is if you want to explicitly have control of setting the session, error messages and redirecting the request. Any other information can be found under http://passportjs.org/docs