This is a small middleware that i created to skip authentication during tests on my nodejs app:
authentication(auth) {
if (process.env.NODE_ENV !== 'test') {
return jwt({
secret: new Buffer(auth.secret, 'base64'),
audience: auth.clientId
});
} else {
return (req, res, next) => { next(); };
}
}
I am not happy with the way it looks. Is there a more elegant way of accomplishing this ?
I think you are right to not be happy with the way that looks. I think what you really want to be able to do is mock out your authentication from the test code instead of inside your actual application code. One way to do this is via proxyquire.
Then a very simple test could look something like this if app.js requires authentication via var authentication = require('./lib/authentication')
var proxyquire = require('proxyquire');
var app = proxyquire('./app.js', {
'./lib/authentication': function() {
// your "test" implementation of authentication goes here
// this function replaces anywhere ./app.js requires authentication
}
});
it('does stuff', function() { ... });
Related
First off if I am asking a obviously stupid question I apologies in advance.
I have a passport authentication strategy setup currently and it is working ok. The implementation is as follows.
Authentication strategy (authentication.js) :
const passport = require("passport");
const passportJWT = require("passport-jwt");
const params = {
//Params here
};
module.exports = function LocalStrategy() {
let strategy = new Strategy(params, function (payload, done) {
//Logic here
});
passport.use(strategy);
return {
initialize: function () {
return passport.initialize();
},
authenticate: function () {
return passport.authenticate("jwt", {
session: false
});
}
};
};
Using in a route :
const localAuth = require('./authentication/LocalStrategy')();
app.get('/test', localAuth.authenticate(), (req, res) => {
res.json(req.isAuthenticated());
});
In the server.js file
const localAuth = require('./authentication/LocalStrategy')();
app.use(localAuth.initialize());
I am planning to use multiple authentication strategies in a single route and I found this implementation. But rather than having the authentication strategy written in the same server.js I want to have the strategy written in an external file (in my case authentication.js) and refer the strategy in the route as
passport.authenticate(['SOME_OTHER_STRATEGY', 'jwt']
How can I implement this?
Ok apparently I was't trying hard enough,
I didn't have to do any change to my current logic other than serializeUser and deserializeUser. and just use:
passport.authenticate(['SOME_OTHER_STRATEGY', 'jwt'])
that't it.
How can one unit test an express router that is dependent on passport authentication to call the helper methods?
I'm new to express unit testing and I've seen a lot of code that actually hits the server to call the method. But would that not make it an integration test? This ultimately comes down to my lack of understanding on the best practices on express unit testing.
I've tried to just mock out the passport but that didn't work because I need to get to the callbacks. I've also tried using rewire and just try to test the helper methods and that didn't seem to work either, I think, because file is wrapped in module.export.
Any help here would be much appreciated.
File I'm trying to unit test:
module.exports = function (inject) {
var router = require('express').Router();
var app = inject.app;
return router.get('/', app.passport.authenticate('bearer', { session: false }), [editContentCheck, getUser]);
function editContentCheck(req,res,next) {
if(req.authInfo.scope.indexOf('readOwnUser') == -1) {
res.statusCode = 403;
return res.end('Forbidden');
}
return next();
}
function getUser(req, res) {
var authHeader = req.headers.authorization.split(' ');
var token = authHeader[1];
var models = require('../models');
models.AccessToken.getAccessToken(token,function(err,tokenObj) {
models.User.getUser(tokenObj.userId, function(err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false); }
res.send(JSON.stringify(user));
});
});
}
};
Check this repository, it has all You want: https://github.com/num8er/alttab-nodejs-challenge
Also a look at example and implement it as You wish:
1)server.js :
var
http = require('http'),
app = require('./app'); // app.js file
http.createServer(app).listen(8080);
2)app.js :
var
express = require('express'),
app = express();
app.use(require('./routes')); // routes.js file
module.exports = app;
3)routes.js :
var router = require('express').Router();
function editContentCheck(req,res,next) {}
function getUser(req, res) {}
router.get('/posts', app.passport.authenticate('bearer', { session: false }), [editContentCheck, getUser]);
module.exports = router;
4)spec/AppSpec.js :
var
request = require('supertest-as-promised'), // npm i --save-dev supertest-as-promised
app = require('./../app');
var token = "some token here";
describe('App', function() {
describe("Posts", function() {
it('should pass auth check and get posts', function() {
return request(app)
.get('/posts')
.set('Authorization', 'Bearer ' + token)
.expect(200);
});
});
});
p.s. I'm using jasmine as testing framework, but even with mocha it's same style. Because of it's using supertest-as-promised that gets app module and calls the route without creating http object.
p.s.2. it's not unit testing, You're testing the feature, so it's more an integration test to check if all chains of code is properly integrated.
What is the best way to call a function on many but not all requests in a node express app? (An example would be a function which checks if the user is currently logged in)
What I did is to define a module exporting a checkLogin(...) function and to call this function on each corresponding api-request. E.g.:
Module auth:
module.exports = {
checkLogin: function(req, res, next) {
if (req.session.hasOwnProperty('user')) {
//if the user is logged in we pass through
next();
} else if (req.cookies.user == undefined || req.cookies.pass == undefined) {
res.render('login', { title: 'Login' });
} else {
User.checkLogin(req.cookies.user, req.cookies.pass, true, function(o) {
if (o != null) {
req.session.user = o;
next();
} else {
res.render('login', { title: 'Login' });
return;
}
});
}
}
};
Routes for /index:
//...
var auth = require('../middlewares/auth.js');
//...
router.get('/index', auth.checkLogin, function(req, res) {
//if we passed the auth.checkLogin step we render the index page
res.render('index', {
title: 'Index',
udata: req.session.user
});
});
In another route file:
//...
var auth = require('../middlewares/auth.js');
//...
router.get('/user/someAPICall', auth.checkLogin, function(req, res) {
...
});
Is this the way to go or are there better ways to do that? I could define a middleware function which I could include using app.use(function(){..}) in each route. The problem is that every request for this route would go through this function which is not what I want.
Routers (http://expressjs.com/en/guide/routing.html) are a great way to design your application. You could think of your URL paths as namespaces, and create a router for the namespace that requires user authentication.
Most likely your main /index page won't require immediate redirecting to login, since it's used for presentation purposes; but if required, then just include the auth.checkLogin as you did above.
For everything else where you need your user to be authenticated (e.g. everything under /user/*), you'd better create a scoped router
const router = express.Router();
router.use(auth.checkLogin);
router.get('/someAPICall', fn1, fn2);
router.get('/someOtherAPICall', fn3, fn4);
and then in your parent router or main app, just include the router:
app.use('/user', router);
which is just like defining:
app.use('/user/someAPICall', [auth.checkLogin, fn1, fn2]);
app.use('/user/someOtherAPICall', [auth.checkLogin, fn3, fn3]);
This gives you the advantage of creating modular route handlers - which makes them easier to adjust, reuse, etc. - and at the same time will keep auth.checkLogin, although always executed when the router is entered, just for the paths defined by the router.
In short, the approach would be: "execute function on all routes inside the router, but not on all the app requests".
If you cannot redesign your routes in this way, then yes, you'll always need to include auth.checkLogin in handlers list for the paths you only want to use.
I'd like to add some functionality to /module that gets executed for any matching route under that directory.
So for a given set of routes:
/module/
/module/page
/module/things
/module/things/:thingid
I want code in a router for /module to run for all of the above routes. /module doesn't render, it just fetches some common data and configures the view context. Then one of the other routes runs and renders the page.
Is this possible with koa-router? Or is there a better package?
Edit:
I think maybe I have to do a nested router and add middleware prior to the nesting like so:
var subRouter = new Router();
subRouter.get('/', function *(next){ doSomething(); });
subRouter.get('/page', function *(next){ doSomething(); });
subRouter.get('/things', function *(next){ doSomething(); });
subRouter.get('/things/thingid', function *(next){ doSomething(); });
mainRouter.use('/module',
function *(next) { doPreRouteSteps(); yield next; },
subRouter.routes()
);
Seems to be working, but I'm not sure if this is an ugly hack or what. If there is a better way, please advise.
You might consider using koa-mount to create a separate app, and then mount it under /module. The sub-app can have any pre- or post- middleware you'd like.
var mount = require('koa-mount');
var koa = require('koa');
var koaRouter = require('koa-router');
var subApp = koa();
var router = koaRouter();
router.get('/page', function *(next){ ... });
subApp.use(function*() { ...pre-route stuff... });
subApp.use(router.routes());
var mainApp = koa();
mainApp.use(mount('/module', subApp));
I like this approach because the way it encourages modularity.
koa-router can do this, i guess better.
router.use('/module/', function *(next) {
// code here
next();
});
router.get('/module/page', function *() {
this.body = 'your view';
});
on this sample, everytime the router encounters '/module'. even if there are trailing or additional params on the url. it will run the first function then proceed to any meet condition.
This is Rei Dien's response updated to 2021. Also you can pass an array of routes or "/" which will call your router.use() before any route. Example with array of routes:
router.use(["/user", "/posts"], async (ctx, next) => {
// Your code before routes...
await next();
});
router.get("/user/likes", async ctx => {
ctx.body = "Your rute code...";
});
I'm trying to configure passport-twitter in my locomotive project.
The problem is that nothing happens after hitting the /auth/twitter url.
Edit: I hit the controller but twitter seems to not be invoked.
What I did was set a match to /auth/twitter at routes.js and mapped this to an auth_controller.js
Something like the code below:
routes.js
this.match('auth/twitter/', 'auth#twitter');
this.match('auth/twitter/callback/', 'auth#callback');
auth_controller.js
var locomotive = require('locomotive')
, Controller = locomotive.Controller
, passport = require('passport');
var AuthController = new Controller();
AuthController.twitter = function() {
console.log('[##] AuthController.twitter [##]');
passport.authenticate('twitter'), function(req, res) {};
}
AuthController.callback = function() {
console.log('[##] AuthController.callback [##]');
passport.authenticate('twitter', { failureRedirect: '/show' }),
function(req, res) {
res.redirect('/list');
};
}
module.exports = AuthController;
I really don't know if that's the right way to use it with locomotive, any help will be very appreciated.
Cheers,
Fabio
Passport needs to be configured first. An example on how to do that can be found here. In the case of LocomotiveJS, the obvious place of putting that configuration would be an initializer:
// config/initializers/10_passport_twitter.js <-- you can pick filename yourself
module.exports = function(done) {
// At least the following calls are needed:
passport.use(new TwitterStrategy(...));
passport.serializeUser(...);
passport.deserializeUser(...);
};
Next, configure sessions and initialize Passport:
// config/environments/all.js
module.exports = {
...
// Enable session support.
this.use(connect.cookieParser());
this.use(connect.session({ secret: YOUR_SECRET }));
// Alternative for the previous line: use express.cookieSession() to enable client-side sessions
/*
this.use(express.cookieSession({
secret : YOUR_SECRET,
cookie : {
maxAge : 3600 * 6 * 1000 // expiry in ms (6 hours)
}
}));
*/
// Initialize Passport.
this.use(passport.initialize());
this.use(passport.session());
...
};
Next, configure routes:
// config/routes.js
this.match('auth/twitter/', 'auth#twitter');
this.match('auth/twitter/callback/', 'auth#callback');
Because passport.authenticate is middleware, it's easier to use a before hook in your controller:
// app/controllers/auth_controller.js
...
AuthController.twitter = function() {
// does nothing, only a placeholder for the following hook.
};
AuthController.before('twitter', passport.authenticate('twitter'));
AuthController.callback = function() {
// This will only be called when authentication succeeded.
this.redirect('/list');
}
AuthController.before('callback', passport.authenticate('twitter', { failureRedirect: '/auth/twitter' })};
Disclaimer: I haven't tested the code above, I'm basing it on my own code which I recently used in a project, and which uses passport-local instead of passport-twitter. However, the basics are pretty much similar, apart from the callback-URL which isn't needed for passport-local.