Is there any way to directly access the req object in supertest, while/after the request is being tested? I want to test my passport strategies, so I want to check req.user, req.session, and perhaps others. I know I can test page redirects or flash, as those are what my strategies do, but it seems useful to see if there is a user on the req object, as well. If I do this, I can also check how many users there are at any one time.
I will sign users up with the "local-signup" strategy, which is defined thusly:
'use strict';
// get passport & mongoose
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var UserModel = require('mongoose').model('User');
module.exports = function() {
// signup function
passport.use('local-signup', new LocalStrategy({
passReqToCallback: true // pass the entire request to the callback
},
function(req, username, password, done) {
process.nextTick(function() {
// find a user with the same username
UserModel.findOne({username: username}, function(err, user) {
// if there is an error, log it then return it
if(err) {
console.log("Error finding a user in the database: " + err);
return done(err);
}
// if a user was already found
if(user) {
return done(null, false, "User already exists");
}
// if we get this far, create a new user from the request body
var newUser = new UserModel(req.body);
// save it and sign it in
newUser.save(function(err) {
if(err) {
console.log("Error during signup: " + err);
return done(err);
}
return done(null, newUser);
});
});
});
}
));
};
One way I use this strategy is like this:
My "local" strategy is defined like this:
'use strict';
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var UserModel = require('mongoose').model('User');
module.exports = function() {
// create our local passport strategy & use it
passport.use(new LocalStrategy({
// use the default names for the username & password fields
usernameField: 'username',
passwordField: 'password'
},
// main strategy function
function(username, password, done) {
// find user with given username
UserModel.findOne({
username: username
},
// with this username, do this
function(err, user) {
// if there's an error, log it then pass it along
if(err) {
console.log("Error during login: " + err);
return done(err);
}
// if the username and/or password is incorrect, return an error
// along with a message
if(!user || !user.authenticate(password)) {
return done(null, false, {
message: 'Invalid username and/or password'
});
}
// if everything is correct, return the user document from the database
return done(null, user);
});
}
));
};
I use both strategies like this, for example:
app.route(pageName).post(function(req, res, next) {
passport.authenticate(strategyName, function(err, user, info) {
if(err || !user) {
res.status(401).send(info);
}
else {
req.login(user, function(err) {
if(err) {
res.status(400).send(err);
}
else {
res.send(null);
}
});
}
})(req, res, next);
});
I tried
request = require('supertest');
this.authServer = require('../my-server');
request(this.authServer)
.put('/signup')
.set('Content-Type', 'application/json')
.set('Host', 'konneka.org')
.send(this.fullUser)
.end(function(req, res, done) {
console.log(res);
});
The res object I logged, inside the end() function, which was way too long to show here, has a req object defined on it, but it seems to only have the objects & functions that were defined before the request was opened. In other words, it does not have req.user, req.session, or other objects I want, because they are defined after the request completes and a new request is started. I noticed it has status codes, as well, which are only defined after the request completes, so I must be missing something.
Is there any way to get access to the req object after the request you are testing is ended? Or am I going about this completely the wrong way?
You cannot do what you want using supertest.
Not sure if this helps but I'll add a little context to clarify the answer:
supertest is a wrapper on top of superagent (client side) with some basic hooks into express to start up the HTTP listener. Under the hood, it really is not any different from starting up your express app, waiting for it to listen on a port, making an HTTP request to that port, and parsing the result. In fact, that is exactly what it does.
So essentially supertest only has access to what ever your client would have access to (a browser or some API client). In other words, if it isnt in the HTTP response body, you wont have access to it. req.user and req.sesssion are server side state variables that are (most likely) not in the response (unless you are doing something strange).
If you want to test in exactly the way you describe, you will have to use some alternative strategy of testing, not supertest.
I found this question when I thought I wanted to do this, and for me it worked well to check the status of the user created by the request instead of verifying the content of the req object. You do have access to the full database where I assume you users somehow ends up.
Related
Please , I have setup passport ldapauth which works fine with all parameters, the problem is if the username or password is wrong, the it does not execute further to the verify callback function at all. It just stops. Due to this I cannot give feedback to the users to indicate what is actually wrong. Is there any clue what I am missing?. This is the structure
passport.use('ldapStudent', new LdapStrategy({
usernameField: 'username',
passReqToCallback:true,
server: {
url: '..........',
bindDn: '.............',
bindCredentials: '..........',
searchBase: '..............',
searchFilter: '.............',
searchAttributes: ['givenName','sn'],
tlsOptions: {
ca: [fs.readFileSync('./ssl/server.crt', 'utf8')]
}
}
},
function (req, user, done) {
//now check from the DB if user exist
if(user){
//check if user email exist;
User.findOne({'EmailAddress': user}, function (err, userdata) {
// In case of any error, return using the done method
if (err)
return done(err);
//user exist redirect to home page and send user object to session
if (userdata) {
//userActivity(PostActivity);
console.log(userdata);
return done(null, userdata);
}else {
//new user, add them to the user model
var newUser = new User();
newUser.EmailAddress = req.body.username,
newUser.JoinedDate = Date.now(),
newUser.UserType = 'Student'
newUser.save(function (err, result) {
if (err) {
console.log('Error in Saving NewUser: ' + err);
} else {
console.log(result);
var PostActivity = {
ActivityName: req.res.__('Student Joined'),
ActivityDate: Date.now(),
UserID: result._id,
UserIP: (req.header('x-forwarded-for') || req.connection.remoteAddress ) + ' Port: ' + req.connection.remotePort
};
userActivity(PostActivity);
console.log('User Registration successful');
return done(null, newUser, req.flash('SuccessMessage', req.res.__('You have been successfully Registered')));
}
})
}
});
}else{
return done(null, false, req.flash('ValidationError', req.res.__('Wrong password and/or email address')));
}}));
This is where i actually do the login
router.post('/login', passport.authenticate('ldapStudent', {
successRedirect: '/',
failureRedirect: '/userlogin',
failureFlash: true
}));
The code works well , just as I expect, the parameters for the ldap option object are intentionally omitted.
The problem is when the user credential are not correct, the verify callback does not get executed at all and so, I can not return a flash message for the user to know what is happening
passport-ldapauth does not execute the verify callback if there is nothing to verify which is the case if the credentials are incorrect and the user is not received. This is in general how the strategies tend to work, e.g. passport-local does not execute verify callback if the username or password is missing.
Strategies, passport-ldapauth included, also usually include a (configurable) message for the failure flash. General configurable login failure messages for passport-ldapauth are listed in the documentation. Each of the messages also has a default value so even when not configured the failure flash message is set (given of course that you have flash middleware in use)
Also, you are not supposed to use req.flash() in the callback of the verify function but to supply an info message.
I have installed passport and passport-http.
I am trying to get my NodeJS Application to allow Basic Authentication, using express.
I have defined the following authentication function for testing purposes in accessControl/users:
var auth=function(user, callback){
console.log("Checking Credentials for "+user)
if (user === 'chuck') {
var userObj = { name:'chuck' };
userObj.validPassword = function(password) {
return (password === 'testa');
}
callback(null, userObj);
}
}
This my express route:
var express = require('express');
var users = require('../accessControl/users');
var passport = require('passport')
, BasicStrategy = require('passport-http').BasicStrategy;
var router = express.Router();
passport.use(new BasicStrategy(
function(username, password, done) {
users.auth({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false); }
if (!user.validPassword(password)) { return done(null, false); }
return done(null, user);
});
}
));
router.get('/test',
passport.authenticate('basic',{session:true}),
function(req, res){
//TODO
res.render('index');
});
Accessing /test first gives me a Browser Login Prompt.
Entering chuck as Username and testa as password redirects to my 500 Error page although the file index.ejs exists in my views folder and is send when I comment out the passport.authenticate line.
Trying to log the produced error via app.js gave me {}.
EDIT:
I am running the app with
DEBUG=my-app node ./bin/www inside a terminal
EDIT2:
I get the following output:
my-app Express server listening on port 3000
GET /test 401 18,4333 ms --
GET /test 500 29,882 ms -- 1905
(This is going to be a bit of a stab in the dark because you have not provided any details about the error...)
Could it be that you set session:true but have not initialized the session middleware? Try setting session:false.
Actually, it looks like the problem stems from this line of code:
users.auth({ username: username }, function (err, user) {
In the above line you are passing a custom object to auth but auth excepts a string object. Try replacing the above line with:
users.auth(username, function (err, user) {
I want to update user's ip every time he or she logs in.
I was informed that only middlewares get req, res, and next parameters, but I am using passportjs, which should come first to authorize the user. How should I update user data?
Below is my code block of passport.js using Basic Strategy.
passport.use(new BasicStrategy(
function(username, password, callback){
User.findOne({username:username}, function(err,user){
if(err){return callback(err);}
//no user found with the email
if(!user){return callback(null, false);}
user.verifyPassword(password, function(err,isMatch){
if(err){ return callback(err);}
// password did not match
if(!isMatch){return callback(null,false);}
//success
// UPDATE USER INFO
user.token = jwt.sign(user.email+Date.now(), "testtoken");//should change later
user.last_login = Date.now();
// I AM TRYING TO UPDATE THIS WITH req.ip
user.last_ip = req.ip;
user.save(function(err, user1){
if(err) return callback(err);
return callback(null,user);
});
});
});
}
));
You can configure BasicStrategy to pass the request as first argument:
passport.use(
new BasicStrategy({ passReqToCallback : true }, function(req, username, password, callback) {
...
})
);
This (sadly) isn't well documented, but I believe most Passport strategies support it (see also).
I am working on a login interface using the MEAN stack. I have managed to get it to work using PassportJS. My problem now is I need a way to let my client-side know whether the person logging in is an admin or user(user role). These info are available from my MongoDB.
The flow of my API call is as follow :
app.post('/login', passport.authenticate('local'), authRoutes.loginCheck);
First, it runs the passport.authenticate where it calls the function below
function verifyCredentials(username, password, done) // username & password from what user provide when logging in
{
console.log('VC');
User.findOne({username: username}, function(err, user) //query Mongo
{
console.log(user); // User role is available here, in JSON format
if(user === null) // if no username in database, do this
{
console.log('Username does not exist in database');
}
else
{
user.comparePassword(password, function(err, match) // function written to compare hashed password in Mongo & password provided by user
{
if(match)
{
done(null, {id: username, name: username});
return user; // this is not the correct syntax, but the idea is, I want to send over the user details here, so I can access the role later
}
else
{
done(null, null);
}
});
}
});
}
The verifyFunction is called with this syntax.
passport.use(new LocalStrategy(verifyCredentials));
Once that function is successfully called, the server executes the 2nd part of it which is the loginCheck.
module.exports.loginCheck = function(req, res)
{
console.log('Calling loginCheck route');
// I generate some sort of jwt token here
// payload, body, blah blah blah ...
console.log(req.body);
res.json({
authenticated: req.isAuthenticated(), //built-in authentication function, returns true or false
token: token // sends over token
role: user.role // want to send over something like this
}); // sends all these to client side as JSON
}
Since both functions are in different files, I am unclear if I have to require something or simply just pass an extra parameter to the loginCheck function. I have tried the latter though and it did not work.
One way that I could think of is do another Mongo query in the loginCheck function, but that would be kinda redundant.
Even a specific keyword for me to google up would definitely be of big help as don't I know what I should be looking for. The reason is because I am new to NodeJS, thus I am not familiarize with most of the terms yet.
I think these codes should suffice but if I am needed to provide more, let me know and I will do so. Thanks in advance !!
To pass control to next matching route you need to use next that passes as third argument in the routes:
function verifyCredentials(req, res, next) {
User.findOne({username: req.body.username}, function(err, user) //query Mongo
{
if(user === null) {
return next(new Error('Username does not exist in database'));
} else {
user.comparePassword(req.body.password, function(err, match) {
if(match) {
next(null, {id: username, name: username});
} else {
next(new Error('not match'));
}
});
}
});
}
app.post('/login', verifyCredentials, authRoutes.loginCheck);
I have a nodeJS application using express and passport. Im trying to handle the post requests to passport for a login. Im getting an unknown error in my server console: POST /signup 500
First question, how do I debug this? is there any way for me to get more information about why its 500'ing?
Second question, any idea's why its not working from a quick glance of what I have below. I know its not all the code, but maybe there is something obvious..?
app.js:
..
var passport = require('passport');
require('./config/passport')(passport);
..
var routes = require('./routes/index')(app, passport);
..
Passport.js defines my handling of users and the database. It defines a localStragery called local-signup.
/routes/index.js:
module.exports = function(app, passport) {
..
var express = require('express');
var router = express.Router();
..
router.post('/signup',
passport.authenticate('local-signup', {
successRedirect : '/profile',
failureRedirect : '/signup' })
);
..
return router
}
Update:
My code is actually working, it is putting new users in the database, its just the application is returning 500. Using loads of console.log() and trial and error I have tied it down to what i returning from my passport stratagy. I have the following code wich is being hit:
..
} else {
console.log("-] Email is not found, creating new user");
user = {};
user['email'] = email;
user['password'] = bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
console.log("-] Inserting into db");
userCollection.insert( user, function(err, user) {
if (err) {
console.log("Error: " + err );
console.log("records: " + user );
throw err;
}
console.log("returned user: " + user)
return done(null, user);
its something to do with me returning user. If I get rid of this line and just return instead, it works fine but the call to authenticate thinks it has failed and follow that's redirect rather than the one for success. The issue is that i got this code from an application that makes use of mongoose which wraps the database stuff in an object. I don't want to do that, i want to make the db calls directly myself like the code above.
Any idea's?
You're not making use of the error handling in the API.
Try something like this instead:
passport.authenticate('local-signup', {
successRedirect: '/profile',
failureRedirect: '/login',
failureFlash: true })
... this tells passport to 'flash' any error resulting from the callback (assuming there's one defined as part of the verify stage of local-signup).
Now, assuming local-signup in your application is set up like so:
var local-signup = require('passport-local').Strategy;
... you should be able to sort the error handling pretty easily. Here's a sample from the configuration docs at http://passportjs.org/guide/configure/ -- note how errors are returned:
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));
For me the solution was to not use:
passport.authenticate('local-signup', {
But:
passport.authenticate('local', {
This is the default if you do not set a name when registering your strategy like so:
passport.use(new LocalStrategy({
You can pass a name in if you have multiple LocalStrategies:
passport.use('local-signup', new LocalStrategy({