Passportjs req.logout() not working after moving to separate file - javascript

I am using NodeJS with Express as the backend for a dashboard web app. I have started splitting the backend server code into smaller files (each route is in it's own file). I split the login function successfully, however when I split the logout function not only does this break the logout function, but it also breaks the login function.
login.js
module.exports = function (passport) {
const express = require('express'),
router = express.Router();
//Login
router.post('/login', (req, res, next) => {
passport.authenticate('local', (err, user, info) => {
if (err) {
return next(err);
}
if (!user) {
return res.status(400).send([user, 'Cannot log in', info]);
}
req.login(user, err => {
res.send('Logged in');
console.log('User logged in');
});
})(req, res, next);
});
return router;
};
logout.js
module.exports = function () {
const express = require('express'),
router = express.Router();
//Logout
router.get('/logout', function (req, res) {
req.logout();
console.log('Logged out');
return res.send();
});
return router;
};
require/use in index.js
const loginRoute = require('./routes/login.js')(passport);
exprApp.use('/api', loginRoute);
const logoutRoute = require('./routes/logout.js');
exprApp.use('/api', logoutRoute);
login.js works the same as before when logout.js is not split off so I don't think the issue lies there. Maybe it's because the res.send is never actually sent to the frontend somehow, so it never reaches the .then? Does anyone know how to fix this?
Edit: I am using Vue in the frontend. Here is the code for handling the response. It never seems to reach the callback, and therefore router.push, etc. are not executed it just hangs.
logOut: ({commit}) => {
axios.get("/api/logout")
.then(() => {
router.push("/");
commit('RESET_USER');
commit('RESET_DEALERSHIPS');
commit('RESET_LEADS');
})
.catch((errors) => {
console.log(errors);
});
}
The 'Logged out' text from logout.js is logged to the browser's console, so the endpoint must exist and be reachable by the frontend (and no errors are logged).

I fixed this issue by simply changing the line
exprApp.use('/api/logout', logoutRoute);
To
exprApp.use('/api/logout', logoutRoute());
I don't know why this works but it solves the issue. As a side note, if I change loginRoute to also use the brackets I get an error from Node.

Related

Express.js main router working, but others routers on him not

I have the problem on routers. My main route /weather working, but others routers on him don't.
app.js
const express = require('express');
const weatherRoute = require('./back/routes/weatherRouter.js');
const app = express();
app.use(bodyParser.json());
app.disable('etag');
app.use('/weather', weatherRoute);
weatherRouter.js
const router = express.Router();
router.get('/', async (req, res) => {
try {
const wholeData = await WeatherInfo.find();
res.json(wholeData);
} catch (err) {
res.json({ message: err })
}
});
router.get('/now', (req, res) => {
res.send("ITS NOT WORKING");
});
module.exports = router;
the problem is that localhost:5000/weather working perfect, but when I want to use some other routers on that Route e.g. localhost:5000/weather/now that's not working
Any ideas what I'm doing wrong ?
UPDATED :
it works, when between those routers is no others routers.
e.g.
router.get('/', async (req, res) => {
//working
}
router.post('/:add', async (req, res) => {
//working
}
router.get('/now', async (req, res) => {
//doesnt work
}
If I move /now above /add router it works perfect. Can someone explain why is this happening ?
Define actual path in path section likerouter.post('/weather/now', (re, res) => {
//Handel re
}
I found the solution.
The routers position is matter. Reference to explanation
My last router didn't work, because another router already catched him.
app.get('/:add', function (req, res) {
// this will match all /a, /b .. including /new
res.end('done!');
});
app.get('/now', function (req, res) {
// this is never called
res.end('done!!');
});

How to automate next() call in every route function? (express.js)

Hi I am facing the problem that I need to log each incomming request and the associated responses in my database. My current solution looks like the following:
./routes/customer.js
router.get('/', async (req, res, next) => {
req.allCustomers = await fetchAllCustomers();
res.status(200).send(req.allCustomers);
next(); // <- this is my personal problem
});
./middleware/logging.js
module.exports = function (req, res, next) {
db.query(
`INSERT INTO logging SET ?`,
{
request: JSON.stringify([req.body, req.params]),
response: JSON.stringify(req.response)
}
);
}
routes declaration
module.exports = function(app) {
app.use(express.json());
app.use('/api/customers', customers); // <- ROUTE ./routes/customer.js
app.use(logging); // <- MIDDLEWARE ./middleware/logging.js
}
I already mentioned my problem in my first piece of code. It is really repetitive to call next() in every route manually and I would like to avoid this. I already tried to load the middleware before all routes, call next() in the middleware function and execute my db query afterwards but I do not have the response at this point because of the async functionality.
Is there any way to handle this situation or will I need keep calling next() at the end of each route function?
If you don't want to call next() from your routes, you cannot have middleware run after them. It needs to be placed before. But can you get the response inside a middleware that runs before the route? The answer is yes!
It may be a little hacky, but since your route uses res.send(), you can use that to your advantage. By running before your route, your middleware can hijack that res.send function, to make it do other stuff.
./routes/customer.js
router.get('/', async (req, res, next) => {
req.allCustomers = await fetchAllCustomers();
res.send(req.allCustomers); // We'll hijack this
});
./middleware/logging.js
module.exports = function (shouldBeLoggedFunc) {
return function (req, res, next) {
if (shouldBeLoggedFunc(req)) {
// Store the original send method
const _send = res.send;
// Override it
res.send = function (body) {
// Reset it
res.send = _send;
// Actually send the response
res.send(body);
// Log it (console.log for the demo)
console.log(`INSERT INTO logging SET ?`, {
request: JSON.stringify([req.body, req.params]),
response: JSON.stringify(body)
});
};
}
next();
};
};
routes declaration
function shouldBeLogged(req) {
// Here, check the route and method and decide whether you want to log it
console.log(req.method, req.path); // e.g. GET /api/customers
return true;
}
module.exports = function(app) {
app.use(express.json());
app.use(logging(shouldBeLogged)); // <- Place this before your routes
app.use('/api/customers', customers);
};
when you use express.Router class like you already did and then use this code
app.use('/api/customers', customers);
you don't have to write 'next()' inside callback function in router.get .
there is an example
create a router file named birds.js in the app directory, with the following content:
var express = require('express')
var router = express.Router()
// middleware that is specific to this router
router.use(function timeLog (req, res, next) {
console.log('Time: ', Date.now())
next()
})
// define the home page route
router.get('/', function (req, res) {
res.send('Birds home page')
})
// define the about route
router.get('/about', function (req, res) {
res.send('About birds')
})
module.exports = router
Then, load the router module in the app:
var birds = require('./birds')
// ...
app.use('/birds', birds)

ExpressJS middleware not working when using NextJS Link

I'm using Express routes with Next, on the example below /a should be accessible by authorised people, while /b is public.
... other imports...
const app = next({ isDev })
const handle = app.getRequestHandler()
async function isAuth(req, res, next) {
const token = req.header('x-Auth-Token');
if (!token) return res.status(401).send('Access denied. No token provided.');
req.user = 'Connected!';
next();
}
app.prepare().then(() => {
const server = express()
server.get('/a', isAuth, async (req, res) => {
return app.render(req, res, '/a', req.query)
})
server.get('/b', async (req, res) => {
return app.render(req, res, '/b', req.query)
})
server.all('*', (req, res) => {
return handle(req, res)
})
server.listen(port, err => {
if (err) throw err
console.log(`> Ready on http://localhost:${port}`)
})
})
Pretty easy and straightforward, for now I'm correctly getting my access denied on the /a using the url bar of the browser except when I use a <Link href="/a"> from my /b page. Then the page shows the hidden content and my access has not been checked... why? How can I resolve this issue?
This issue can be replicated using this Github link, you will just need to add the isAuth example as I did on the example above.
That is part of how the Next.JS Link works. It already pre-fetches the sources for the upcoming site, without ever fetching against the real endpoint, thus you are required to implement both frontend and backend checks for your current situation.
For further information feel free to follow this discussion within Next.JS Github Issue: Github NextJs Restricted Links. It clearly explains how to deal with such a situation.

JWT not working with express router

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.

Trouble with authentication

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

Categories

Resources