Passportjs exclude resources - node.js

I'm working an API endpoint with expressjs and protecting it with passportjs (using jwt strategy)
I have
passport.use(new JwtStrategy({...})) // Register stratrgy
router.post('/login', (req, res) => {} ) // Generates jwt token
app.use('/api', passport.authenticate('jwt', { session: false }));
app.user('/api', router) // Register the route to /api
If I put /login out of /api, this works just fine. But I want to have some resource like /login in the same root path (/api) to be not protected.
I can go ahead and add passport.authenticate('jwt', { session: false }) to every route registration except the unprotected once, but I don't like that.
I can also create a middleware that call passport.authenticate manually and check other things, but I would like to avoid re-inventing the wheel if it is already done.

Have you tried creating a route like:
app.post('/api/login')
And then after that you define your router like:
app.user('/api', router) // Register the route to /api
Finally inside your router file, at the top, you do
router.use(passport.authenticate('jwt', { session: false }))

Related

Getting "ForbiddenError: invalid csrf token" when splitting routes into different files/modules

I'm using csurf in my express project. I have 3 files:
app.js - Main entry point
routes/index.js - Index routes
routes/users.js - User routes
It's standard boilerplate when using express application generator.
I have a route in index.js:
router.get('/', csrfProtection, function(req, res, next) {
res.render('index', {
csrfToken: req.csrfToken()
});
});
The page for this route contains a form that has a hidden field with csrf token:
input(name='_csrf', type='hidden', value='#{csrfToken}')
All works fine and I can see the csrf token in the source.
When the form is submitted it's handled buy the route in routes/users.js:
router.post('/login', csrfProtection, function(req, resp) {
if(!validator.isAlphanumeric(req.username))
console.log('Not alphanumeric');
...
});
It appears the problem is something to do with both files having to create new instances of csrf, and csrfToken. At the head of both route files I require them like so:
var csrf = require('csurf');
var csrfProtection = csrf({ cookie: true });
If I put the login route into routes/index.js it works fine, which has made me think maybe both instances are using different csrf tokens.
Any ideas?
Yes, I believe it is using different CSRF tokens. I got around the issue by defining an init function in my sub-modules, and then passing the CSRF token into that. That way the CSRF token is only created once. I think creating the CSRF token in app.js is probably best, and then you can pass it into your various sub-modules.
Ex:
in users.js:
function init(router, csrfProtection) {
router.post('/login', csrfProtection, function(req, resp) {
if(!validator.isAlphanumeric(req.username))
console.log('Not alphanumeric');
...
});
}
module.exports.init = init;
in app.js:
...initialize router and CSRF protection...
var users = require('./users.js');
users.init(router, csrfProtection);

Express route bundling

My routes.js file
module.exports = function(app) {
//donesn't need to be authorized
app.use('/login', require('./controllers/session'))
//will need to be protected
app.use('/user', auth, require('./controllers/users'))
app.use('/vineyards', auth, require('./controllers/vineyard'))
app.use('/varietals', require('./controllers/varietals'))
app.use('/wines', auth, require('./controllers/wine'))
app.use('/spectrums', auth, require('./controllers/spectrum'))
app.use('/lockers', auth, require('./controllers/locker'))
app.use('/logout', auth, require('./controllers/session'))
}
The 'auth' middleware works when I make a get, or jsonp request, but if I use post it errors out, with a statusCode of 0, and content 'XMLHttpRequestProgressEvent XMLHttpRequestProgressEvent'. However if I go in the actual controllers and put the auth middleware on the actual post like below, it works.
router.post('/', auth, function(req, res) {});
Why is that? Is there a better way to make the middleware run before all calls to a controller then putting it on each individual route?

Use app.all as oauth authorization middleware in node.js

I can't figure out how to use app.all to act as oauth authorization filter for all the routes starting with /api/
passport.authenticate('bearer', { session: false });
is used for authorization. and here are two standard get configurations:
app.get('/api/foo', foo.find);
app.get('/api/bar', bar.find);
I don't want to include it in every call like:
app.get('/api/foo', passport.authenticate('bearer', { session: false }), foo.find);
It's simple, just get it to catch all routes starting with api. Make sure you put this before your routes.
app.all('/api/*', passport.authenticate('bearer', {session: false});

How to protect static folder in express with passport

I have a project based on express with a required authentication based on passport.
The backoffice is an angularjs app served as static files.
My authentication code is completly based on https://github.com/jaredhanson/passport-local/blob/master/examples/express3-no-connect-flash/app.js
To do not serve the angular app if you are not authenticated. I have try by adding ensureAuthenticated on the /admin route but it make the route not working (404). Once I remove ensureAuthenticated the /admin is served.
app.use(express.static(path.join(__dirname, 'public')));
app.use('/admin', ensureAuthenticated, express.static(path.join(__dirname, 'admin')));
//serve routes
app.use(app.router);
The public folder contains the login page.
How could I achieve this ?
Ran into same issue, this is what I ended up doing!
app.use doesn't let you chain middlewares in that way. The various
app.VERB functions do, but app.use doesn't. That's for one middleware
at a time.
If you split the 2 middlewares out into separate calls, you should get
the results you want:
app.use('/admin', ensureAuthenticated);
app.use('/admin', express.static(path.join(__dirname, 'admin')));
Cannot use basic authentication while serving static files using express
You can check the route using middleware and redirect them if they aren't logged in and are hitting admin pages, something like (untested):
app.use(function(req, res, next) {
if (req.user == null && req.path.indexOf('/admin') === 0)
{
res.redirect('/login');
}
next();
});
app.use('/admin', function(req,res,next){
if(req.user){
return express.static(path.join(__dirname, 'public'));
} else {
res.render(403, 'login', {message:'Please, login!'});
}
});
//serve routes
app.use(app.router);
Update for express#4.16.4+, passport-jtw#0.4.0, and passport-jwt#4.0.0
First setup a passport auth strategy. If you use a jwt, you can take a token from a query parameter, if not you can use another Extract function (or multiple using Jwt.ExtractJwt.fromExtractors())
passport.use('basic-user',
new Jwt.Strategy({
...jwtConfig.options,
jwtFromRequest: Jwt.ExtractJwt.fromUrlQueryParameter('token')
}, verifyUser)
);
Then you can use a passport authenticate function before serving static files
app.use('/files', [
passport.authenticate(['basic-user'], { session: false }),
express.static(path.join(__dirname, 'files')) //make sure you access proper directory
])
You could also chain middlewares as an array to achieve this goal:
app.use('/admin', [ensureAuthenticated, express.static(path.join(__dirname, 'admin'))]);
The accepted answer felt a bit partial (and may not work in some cases), so here's a bit more verbose and generalized answer:
// Here we'll attach the user object. The correct ordering
// of these middleware functions is important.
app.use(
'/some-restricted-static-path',
passport.authenticate("bearer", { session: false })
);
// As the user object should now be attached (if authorized), we can now
// verify it to be so.
app.use('/some-restricted-static-path', (req, res, next) => {
if (!!req.user) {
// The user exists, we can continue.
// Here you can also validate the role etc if necessary.
next();
} else {
// No user object found, terminate the pipeline with .end().
res.status(401).end();
}
});
// And finally, here's the actual handler that won't be accessed if
// something went wrong earlier.
app.use(
'/some-restricted-static-path',
express.static(
path.join(
__dirname,
"../dist/attachments"
)
)
);
Explanation: In Express, the middleware is processed one-by-one. If one of the middleware terminate the process, every middleware after it will be skipped. So, knowing this, we can first attach the user object, then validate it, and finally either grant or deny access.

How to use the middleware to check the authorization before entering each route in express?

I want to check the authorization of the users of my web app when they entered the url. But when I used an individually middleware to check the authorization, it's useless for the already existing routes, such as:
function authChecker(req, res, next) {
if (req.session.auth) {
next();
} else {
res.redirect("/auth");
}
}
app.use(authChecker);
app.get("/", routes.index);
app.get("/foo/bar", routes.foobar);
The authChecker is unabled to check the authority of the users who entered the two urls.
It only works for the unspecified urls.
And I saw a method that I can put the authChecker between the route and the route handler,
such as:
app.get("/", authChecker, routes.index);
But How can I achieve it in a simple way rather than putting the authChecker in every route?
As long as
app.use(authChecker);
is before
app.use(app.router);
it will get called for every request. However, you will get the "too many redirects" because it is being called for ALL ROUTES, including /auth. So in order to get around this, I would suggest modifying the function to something like:
function authChecker(req, res, next) {
if (req.session.auth || req.path==='/auth') {
next();
} else {
res.redirect("/auth");
}
}
This way you won't redirect for the auth url as well.
There are may ways to approach this problem but here is what works for me.
I like to create an array of middleware for protected and unprotected routes and then use when necessary.
var protected = [authChecker, fetchUserObject, ...]
var unprotected = [...]
app.get("/", unprotected, function(req, res){
// display landing page
})
app.get("/dashboard", protected, function(req, res){
// display private page (if they get this far)
})
app.get("/auth", unprotected, function(req, res){
// display login form
})
app.put("/auth", unprotected, function(req, res){
// if authentication successful redirect to dashboard
// otherwise display login form again with validation errors
})
This makes it easy to extend functionality for each middleware scopes by editing the array for each type of route. It also makes the function of each route more clear because it tells us the type of route it is.
Hope this helps.
But when I used an individually middleware to check the authorization, it's useless for the already existing routes
Express will run middleware in the order added to the stack. The router is one of these middleware functions. As long as you get your authChecker into the stack BEFORE the router, it will be used by all routes and things will work.
Most likely you have the router before authChecker because you have routes defined prior to getting your authChecker into the stack. Make sure to put all your app.use calls before any calls to app.get, app.post, etc to avoid express's infuriating implicit injection of the router into the middleware stack.

Resources