Special Passport Authentication for Specific Route - node.js

I'm developing a RESTful API with Express.js and I'm using Passport.js for authentication purposes.
Here is an example of how I'm using passport with routes:
const beerRoutes = require('./beerRoutes') // an instance of express router
let authenticateRoute = passport.authenticate('jwt', { session: false })
router.use('/beer', authenticateRoute, beerRoutes)
Inside beerRoutes.js :
const router = require('express').Router()
router.post('/', (req, res) => {})
router.get('/', (req, res) => {})
router.patch('/', (req, res) => {})
router.delete('/', (req, res) => {})
The problem is, I want unauthenticated clients to be able to create new users (i.e. POST /beer/).
But I also want to give additional permissions to authenticated clients when they send a request to the same endpoint.
How can I achieve this without letting unauthenticated clients to access other routes inside beerRoutes (e.g. PATCH /beer/)?

This can be addressed with a custom middleware which calls the Passport.js middleware.
const authenticationWhiteList = [
'POST /beer'
]
function Authenticate (request, response, next) {
let route = `${request.method} ${request.baseUrl}`
if (_.indexOf(authenticationWhiteList, route) !== -1) {
next()
} else {
passport.authenticate('jwt', { session: false })(request, response, next)
}
}
Then change the authentication code to this one:
const beerRoutes = require('./beerRoutes') // an instance of express router
// let authenticateRoute = passport.authenticate('jwt', { session: false })
let authenticateRoute = Authenticate
router.use('/beer', authenticateRoute, beerRoutes)

Related

Cannot set headers after they are sent to the client using a middleware in nodejs

I'm getting this error
Cannot set headers after they are sent to the client
when I checkif an user is authenticated with a middleware
var isAuthenticated = function (req, res, next) {
if(!req.isAuthenticated()) res.redirect(301,'/login');
next();
};
module.exports = isAuthenticated;
and this is the route function
router.get('/', isAuthenticated,(req, res) => {
res.render('main/index.twig');
})
Try updating your if condition to return. Otherwise it could be trying to call redirect and then next.
if(!req.isAuthenticated()) return res.redirect(301,'/login');

ExpressJS: How to conditionally start session?

This may sound like a really simple/silly question, but I can't find anything about it on the web.
I'm using Express 4 with NodeJS and trying to implement session using express-session middleware. I want to use session to allow/disallow certain routes for different users.
I know I can use session on conditional route:
const express = require('express');
const Router = require('express').Router;
const router = new Router();
const session = require('express-session');
const sessioning = session({...});
router.post('/login', sessioning, (req, res) => {
});
But that's not what I'm trying to do. That will start a session even for failed login attempt.
What I'm trying to do is to start the session only after a successful login attempt:
const express = require('express');
const Router = require('express').Router;
const router = new Router();
const session = require('express-session');
router.post('/login', (req, res) => {
/* ... Login validations */
if (login === 'success'){
/* ... start session */
}
res.json({...});
});
So that I can disallow unauthenticated users from protected routes:
router.get('/protected', (req, res) => {
if (!req.session){
res.status(401);
res.end();
return;
}
/* ... */
});
If I set session directly on protected routes, then it can't verify whether or not a user is logged in.
Can anyone point me to the correct direction?
This does not answer your primary question but address your (seemingly) main concern:
If I set session directly on protected routes, then it can't verify whether or not a user is logged in.
You can attach a variable to the req.session object to check if user is logged in.
Set loggedIn in status in your login route
router.post('/login', (req, res) => {
/* ... Login validations */
if (login === 'success'){
req.session.loggedIn = true;
}
// ...
});
And set up a middleware that checks wether the user is logged in and protect your routes.
function checkLoggedIn(req, res, next) {
if (req.session.loggedIn)
next();
else
res.redirect('/login')
}
// Your protected route
router.get('/protected', checkLoggedIn, (req, res) => {
// ...
});

Passport JS authentication returning false when logging in from front-end side

I am currently developing a website using MERS stack. I am using express-session and passport.js for my back-end authentication. When I try to log in from my back-end server, the API works fine. However, when I try to send a POST request from my client-side (React), it is not authenticated.
I have tried to console.log the request from front-end and back-end, and the request is identical. One thing I notice is when I do not put an authentication middleware in my API, my front-end gets the data after being redirected to the API; the opposite happens when I put the middleware.
//This is my POST code
router.post(
"/userLogin",
passport.authenticate("local", {
successRedirect: "/api/user",
failureRedirect: "/api/user/asktologin"
}),
(req, res) => {}
);
//This is my middleware
const isLoggedIn = (req, res, next) => {
if (req.isAuthenticated()) {
console.log(req.isAuthenticated);
} else {
console.log(req);
}
};
Your isLoggedIn middleware is not calling the next function in the stack. It should look like this
const authenticationMiddleware = (req, res, next) => {
if (req.isAuthenticated()) {
console.log(req.isAuthenticated);
next()
} else {
console.log(req);
res.send(400);
}
};
// Then you configure it like so
app.use(authenticationMiddleware);
// Your "router" config goes here
post("/userLogin",
passport.authenticate("local", {
successRedirect: "/api/user",
failureRedirect: "/api/user/asktologin"
}),
(req, res) => {
// Do stuff
}
);
For more detailed information on how to use middlewares, make sure you check out the docs.

Protect express.js endpoint with passport

I would like to protect some endpoints in my express app, I want to create something simple to manage if my app became a big app...now I'm doing something like this:
setProtected(router) {
const self = this;
router.use(this.auth);
...
}
setPublic(router) {
const self = this;
...
}
getRouter() {
const router = express.Router();
this.setPublic(router);
this.setProtected(router);
return router;
}
with:
auth(req, res, next) {
if(req.isAuthenticated()) {
console.log('req.isAuthenticated()', req.isAuthenticated());
return next();
}
return res.send(401);
}
the problem in this case is that is difficult maintain and it doesn't work well as if I have /:id in my publicRoute and for example /my-items in my protected route when I'm not logged and I try to reach /my-items I get the code of /:id.
Another idea was to create a json with the list of all my urls with same information like protected/not protected and eventual roles and then change auth with something like:
import urls from './urls';
auth(req, res, next) {
if (urls[req.url] == 'public') {
return next()
}
else if (urls[req.url] == 'protected' && req.isAuthenticated()) {
return next();
}
return res.send(401);
}
whats the best way for you?
You can chain middlewares:
eg.
const authenticate = (req, res, next) {
.. some auth logic
next();
}
app.use('/', main...
app.use('/profile', authenticate, otherMiddleware,
app.use('/admin', authenticate, isAdmin, otherMiddleware...
in your main file (server.js) import the routes and use the middleware there :)
server.js
const express = require('express')
const cors = require('cors')
const app = express()
// import admin routes
const adminRoute = require('./app/routes/admin.route.js')
// Add middleware for parsing URL encoded bodies (which are usually sent by browser)
app.use(cors())
// Add middleware for parsing JSON and urlencoded data and populating `req.body`
app.use(express.urlencoded({ extended: false }))
app.use(express.json())
// homepage route
app.get("/", (req, res) => {
res.json({ message: "Hello World" })
})
// restricted by middleware "isAdmin"
app.use('/api/v1', isAdmin, adminRoute)
app.listen(8008).on('listening', () => {
console.log('Server is running on 8008')
})
admin.route.js
const express = require('express')
const admin = require('../controllers/admin.controller.js')
const router = express.Router()
// get all admin users
router.get('/users', (req, res, next) => {
admin.getAdminUsers(req, res, next)
})
module.exports = router

Express.js ERR_TOO_MANY_REDIRECTS error

Here's the scenario.
If a user wants to see a page that requires user access, I want to redirect him to the homepage, but I get ERR_TOO_MANY_REDIRECTS error when I try to access the homepage.
I couldn't figure it out how to solve this.
app.js
exports.ensureAuthenticated = function (req, res, next) {
jwt.verify(req.cookies.userToken, "NICETRY", function (err, decoded) {
if (err) {
res.redirect("http://localhost:4000");
} else {
// no err
if (decoded.id) {
req.id = decoded.id;
req.iat = decoded.iat;
next();
} else {
res.send("invalid cookie");
}
}
});
};
routes/frontend/index.js
var express = require('express');
var router = express.Router();
var indexController = require('../../controllers/frontend/indexController');
var auth = require('../../app').ensureAuthenticated;
router.get('/', auth, indexController.index);
module.exports = router;
indexController.js
exports.index = function (req, res) {
res.render('frontend/home/index');
};
The issue is that you're redirecting back to the same page once authentication fails and so your ensureAuthenticated middleware runs again and redirects again.
Try making one authenticated route and one unauthenticated route.
router.get('/', indexController.index); // no auth necessary
router.get('/private', auth, indexController.private); // requires auth
Now if you fail auth when you visit /private it will redirect to / which will display to the unauthenticated user.

Resources