Passportjs multiple authentication strategies external file with expressjs - node.js

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.

Related

How do I share functionality between my expressjs routes?

Suppose I have two routes in my expresjss project: users.js and profiles.js.
users.js:
var express = require('express');
var router = express.Router();
/* GET users listing. */
router.get('/', function(req, res, next) {
var extra = something_nice();
res.json( { three: 'four', extra: extra } );
});
module.exports = router;
profiles.js:
var express = require('express');
var router = express.Router();
/* GET profiles listing. */
router.get('/', function(req, res, next) {
var extra = something_nice();
res.json( { one: 'two', extra: extra } );
});
module.exports = router;
Notice how I have something_nice() method there, which ideally I would define in a 'super class' if this were regular OOP like rails controllers.
How do I go about this with node + expressjs? My assumption was I should create a new module, and require it here, but is this the best practice?
You solve it in the same way. Using a class:
class SomethingCool {
somethingNice() {
return 'cool!';
}
}
module.exports = SomethingCool;
In your route:
var express = require('express');
var router = express.Router();
var SomethingCool = require('./something-cool.class');
const something = new SomethingCool();
/* GET profiles listing. */
router.get('/', function(req, res, next) {
var extra = something.somethingNice();
res.json( { one: 'two', extra: extra } );
});
module.exports = router;
Same principles should apply regardless of language (as long as they have support for classes, or class-like objects).
Also you don't even need a class here:
function somethingNice() {
// some logic
}
// inside file1
router.get('/', (req, res, next) => {
const extra = somethingNice();
res.json({ one: 'two', extra });
});
You can reuse functions where you like/need, just ensure they're exported using module.exports (if using in a different file/module).
Have a read over this when you have time:
https://dev.to/santypk4/bulletproof-node-js-project-architecture-4epf
It may be able to answer some of the questions you later have about design, structure and reusing logic in different areas.
EDIT: an explanation on how middleware can help in certain situations.
https://expressjs.com/en/guide/using-middleware.html
function isUserAdmin(user) {
// some logic for determining
}
app.use((req, res, next) => {
if (isUserAdmin(req.user)) {
req.role = 'Admin';
}
next();
});
This is a simplified example, in reality you'd also need to add the req.user.
The thing to understand here, is your ability to use middleware to reuse functionality.
However, if you wanted something a little more specific to the route, then I'd opt to use a class (following the Service pattern from other reference link).
Another (more common) example, consider a logger, it outputs which endpoint was requested and with what method:
// file: service/logger.service.js
class LoggerService {
log(message) {
console.log(message);
}
}
module.exports = MyLogger;
// file: middleware/logger.middleware.js
const logger = new MyLogger();
app.use((req, res, next) => {
const path = req.path;
const method = req.method;
logger.log(`${path} ${method}`);
return next();
});
This way, your route never needs to know about the logger, or what function it has, you can plug an unlimited amount of additional functionality this way.
Although it's more suited for generic tasks, like checking a user has authenticated (for example), or is authorized, but it's certainly not limited to only that type of use.
If you really want a super class, then use a singleton:
class SuperClass {
constructor() {
this.someOtherClass = new UserClass();
this.someSecondClass = new ProjectClass();
}
doSuperWork() {
}
}
module.exports = new SuperClass();
I'll say though, it may not be the best solution (super classes in general).
You can create a function that accepts the router instance as an argument and can implement your common logic inside that function. Javascript uses composition instead of inheritance.
module.exports=function (router){
router.get('/', function(req, res, next) {
var extra = something_nice();
res.json( { one: 'two', extra: extra } );
});
}

Best practices for building singletons in node.js

I am trying to build an express app and I need to create some singletons (like db object in Sequelizer).
app.js
app.use(...);
app.use(...);
var serviceLocator = {
foo: require('foo'),
bar: require('bar')
}; //object holding singletons.
app.use('/api/todo', new todoRoutes(serviceLocator));
todoRoutes.js
module.exports = (serviceLocator) => {
var router = express.Router();
router.get('/', (req,res,next) => {
//use serviceLocator.foo
});
router.get('/:id',(req,res,next) => {
//use serviceLocator.bar
});
};
Is this a good practice?
(I've also read about building singletons using require caching, but I have concerns since in the official docs they say that require "may not" return the same object).
How I usually do it looks something like this:
app.js
const db = require('./path/to/db/singleton');
const sql = require('./path/to/Sequelizer/singleton');
app.use(...);
app.use((req, res, next) => {
req.db = db;
next();
});
app.use((req, res, next) => {
req.sql = sql;
next();
});
app.use('/api/todo', require('./todoRoutes');
todoRoutes.js
const express = require('express');
const router = express.Router();
router.get('/', (req,res,next) => {
console.log(req.db);
});
router.get('/:id',(req,res,next) => {
console.log(req.sql);
});
module.exports = router;
The overall is just to add some middleware that adds it to the req, which is going to go through the pipeline for you. You could namespace it too by adding it to req.server.<thing> or something.
You can also do this to generate a request ID by bringing in uuid and attaching an ID to every req in another middleware that you can use in your log statements later, that way you can track requests through.
Alternatively, just require those singletons into the todoRoutes.js and use them directly. They're singletons on your server, so I don't see any issue with that.

How to elegantly skip express-jwt middleware when running tests?

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() { ... });

How to unit test express route with passport authenticate

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.

Best way to setup locomotivejs with passport-twitter?

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.

Resources