I'm using koa-passport & koa to handle my login and registration using twitter oauth. It works great, but I'm having difficulties understanding how I should test my authenticated routes using supertest and mocha.
Most examples I have seen involve using supertest to send a username and password to a login endpoint to first create a user session. My problem is that I don't have a username/passport auth strategy. I need to emulate an oauth login process that will set the appropriate session variables, and therefore test certain routes.
Any clue on how I can achieve this?
My solution these days to this problem is to basically insert a mock authenticated user in the middleware stack temporarily when needed. Here's my basic code for express.
var Layer = require('express/lib/router/layer');
var app = require('../../../server');
exports.login = login;
exports.logout = logout;
function login(user){
var fn = function insertUser(req, res, next){
req.user = user;
next();
}
var layer = new Layer('/', {
sesitive: false,
strict: false,
end: false
}, fn);
layer.route = undefined;
app._router.stack.unshift(layer);
}
function logout(){
app._router.stack.shift();
}
And then within your tests, you call:
it('should allow the user to edit something', function(done){
login(this.user);
// perform supertest task
logout();
});
This is obviously pretty rudimentary... but seems to do the job for testing purposes.
Related
I have a NodeJS application which I have begun to separate out in to smaller files since the original became a little bloated.
In my index.js I have routes that are protected by a function a freelancer wrote to provide JWT authentication. These routes work as required.
app.use(require('./lib/api-calls/convert.js'));
// Security Enabled index.js
//
const { app } = require ('./lib/deps/init_dependencies.js');
const { enableSecurity } = require("./security");
const main = async () => {
// Enable API JWT Security = Comment out the line below to turn off security.
await enableSecurity(app);
app.get('/v1/createSession:key/:limit', function (req, apiResponse) {
// My route, working well
});
}
main()
I've created /lib/routes/convert.js and am wanting to write new routes in this file which also require JWT authentication. However, I always receive status 200 'OK', regardless of whether the authentication header is correct or not... I'm using Postman to make my calls. Here's my code:
const app = require('express')();
//JWT authentication
const { enableSecurity } = require('../../security');
const main = async () => {
// Enable API JWT Security = Comment out the line below to turn off security.
await enableSecurity(app);
app.get('/v3/convertw3w/:locationValue/:countryCode', function (req, res) {
res.status(200).send({ status: 'OK' });
});
}
main()
module.exports = app;
Can anyone spot the problem? I spent far to long on this last night!
thanks
Just some food for thought here and we do something similar, but if you use
enableSecurity(app)
As middleware on the route and the
next()
function in the middleware you can omit the need to make this a promise, because middelware is designed to process in order of the middleware and the next function tells express to move to the next middleware.
How we do it, is to have the middleware 'auth', because middleware will pass the req and res objects to each one in the stack you can have all your JWT decode in one place.
We typically will pass the token in the header OR the req object, just depends on the mimetype we pass, so in out auth we check if the token is in the header or the req, if so we decode, if it passes decode we pass
next()
in that code block otherwise we res.json({"response":"not authorized"})
I'm trying to test an authenticated endpoint in my app. My node app uses express, express session, passport-local, react and next for auth.
I've spent way too many hours trying to solve this problem and could not find a solution.
Basically my test would like to:
send a login request and login
send a request to an authenticated route
receive the appropriate response
My issue was that I had no persistence between the login request and the authenticated route request.
When I sent the login request, passport serializes the user and sets req.user and req._passport.session to the appropriate values.
On the next request - the authenticated route, my middleware looks for req.passport._session or req.user but neither exist. I would get the 401 unauthorized response.
I posted my solution below that took me way too long to figure out.
I solved the persistence issue with Chai HTTP - which uses superagent.
The solution was pretty simple once I had the correct tools. I used the tutorial from the chai-http page but changed it to use async await and try catch.
const { assert } = require('chai');
const chai = require('chai');
const { expect } = require('chai');
const chaiHttp = require('chai-http');
chai.use(chaiHttp);
describe('/authenticatedRequest', () => {
it('should respond appropriately', async () => {
const agent = chai.request.agent('http://localhost:8000');
try {
await agent
.post('/rootPath/loginLocal')
.send({ email: 'email#email.com', password: 'password' });
const authenticatedResponse = await agent.get('/rootPAth/authenticatedRoute');
assert.deepEqual(successResponse, workoutRes);
} catch (e) {
console.log(e);
}
});
});
I now see this post How to authenticate Supertest requests with Passport? - which would have saved me a lot of time.
I hope adding having another post will help someone else with this.
I have implemented the OAUth 2.0 flow as single authentication method for my express application.
In real situation, once a user connected using OAuth, a session is created and accessible server-side in req.session.passport.
In this session, there is the OAuth access token that is required to perform authenticated requests on my REST API. To be able to do so, I wrote a middleware that copies the token from req.session.passport to the Authorization header.
app.use(function(req, res, next) {
if (req.session.passport) {
req.headers['Authorization'] = req.session.passport.user.accessToken;
}
next();
});
Now, I would like to test this middleware without going through the entire oauth flow. Ideally, I would like to create a session manually and inject test data inside req.session.passport, then call any protected endpoint of my API to check that it can correctly perform authenticated requests.
I tried to manually set data inside req.session in the following test
it('Should be able to make an authenticated request with session data',
function(done) {
var req = request(app).get('/api/SomeProtectedEndpoint');
req.session = {
passport: {
user: {
accessToken : 'eade123d3ffwhatever'
}
}
}
req.expect(200, function(err, res){
if (err) return done(err);
done();
});
});
But if I display the contents of req.session inside my middleware, the manual data is gone. I believe it is removed by express-session.
How can I manually inject session data in a request for testing purposes ?
Auth0 documentation describes how to set up express-jwt middleware to protect endpoints. The trouble is that the documentation doesn't seem to cover how you get a valid JWT in the first place.
On the angular side, there's documentation on using angular plugins to implement a login page, that's fine. How would one implement a route using express that would take a username/password and return to the client the appropriate JWT such that subsequent requests would be authorized?
I think I may be missing a basic concept about JWT here; via Auth0, when using Username-Password-Authentication, my guess is that Auth0 acts as the repo for those credentials. There's documentation out there about wiring passport to auth0 and JWT, the problem with those is that this documentation assumes that the username/password database is some MongoDB instance locally...I want to avoid that type of setup which was an initial attraction with auth0.
Are there sample projects that cover this, showing how to get a valid JWT on a back-end, without some separate front-end angular app requesting it first?
I use passport.js built in local strategy for authentication and store user information in a JWT that I read on routes that require authorization.
User id's can be serialized/deserialized into and out of the express sessionto obtain the user identifier using the auth token (JWT) in the request. This is in my opinion the best approach since it limits the amount of data stored on the client and provides better security than storing any user information. Here's an example of this in express:
//Set a session secret
var secrets = { sessionSecret: process.env.secret || 'my secret string'};
//Require express-jwt and set a secret for the cookie
var expressJwt = require('express-jwt');
var validateJwt = expressJwt({ secret: secrets.sessionSecret });
//Returns a jwt token signed by the app secret
var signToken = function(id) {
return jwt.sign({
id: id
}, secrets.sessionSecret, {
expiresInMinutes: 60 * 24 // 24 hours
});
};
//Set token cookie directly
var setTokenCookie = function(req, res) {
if (!req.user) {
return res.status(404).json({
message: 'Error during user validation'
});
}
var token = signToken(req.user.id, req.user.role);
res.cookie('token', JSON.stringify(token));
};
//Check to see if user is authenticated (call this when a route is requested)
var isAuthenticated = function(req, res, next) {
// allow access_token to be passed through query parameter as well
if (req.body && req.body.hasOwnProperty('access_token')) {
req.headers.authorization = 'Bearer ' + req.body.access_token;
}
// Validate jwt token
return validateJwt(req, res, next);
};
You can use these methods as middleware in express. Say the above code was token.js, you can force it to execute on each request to a route like this:
app.get('/employee', token.isAuthenticated, employeeController.getEmployees);
I haven't worked with angular but it works great on the backbone projects i've worked on and this process should work with any browser based client that can supply a X-auth cookie on each request. You can do this by using the ajax setup:
$(document).ajaxSend(function(event, request) {
var token = readCookie('token');
if (token) {
request.setRequestHeader('authorization', 'Bearer ' + token);
}
});
Here is an example of middleware that validates a users login and returns a token to the client that can be used on subsequent requests:
var validateLogin = function (req, res, next) {
var username = req.params.username;
// Authenticate using local strategy
passport.authenticate('local', function(err, user, info) {
if (err) {
return next(err);
}
if (!user) {
return res.status(404).json({
info: [{
msg: info.message
}]
});
}
// Send user and authentication token
var token = token.signToken(user.id, user.role);
res.cookie('token', token);
res.render('index', {token: token, user: user});
})(req, res, next);
};
#FrobberOfBits
This is to answer the follow-up Q posted by FrobberOfBits on Feb 6, 2016 at 3:04
I use auth0 for local + social media authentication.
The way auth0 works is, you hand over the approach to authenticate to auth0 ...either it be local with db or social media.
It is a bundled approach where local db and social media authentication is all bundled and provided as a service to you by auth0.
Hope this helps.
I am testing my MEAN stack based app on my local. For authentication feature i am using passportjs.
But while debugging i have to login every time i restart the server.
Is there a easy configuration which i can do to disable passportjs without chaning much of my code.
I got it working with the following piece of code. Only following change was required to skip authentication for my app.
var isAuthenticated = function (req, res, next) {
var isAuthorised = req.isAuthenticated();
// TODO remove followoing on production code
// Set user as authorised in local
isAuthorised = true;
req.user = {};
// id of whichever account you want to load
req.user._id = '12345whatever';
if (isAuthorised) {
return next();
}
// if the user is not authenticated then redirect him to the login page
res.redirect('/login');
};
Hope it helps someone.