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) {
Related
I 'm using a MongoClient connexion pool at the top of my app.js node.js back end, it works well:
var db;
MongoClient.connect("mongodb://localhost:27017", function(err, client) {
if (err) throw err;
db = client.db('myDb');
// Start the application after the database connection is ready
app.listen(3000);
console.log("Listening on port 3000");
});
app.post('/getEvaluations', function(req, res) {
db.collection('evaluations').find().toArray(function(err, docs) {
if (err) throw err;
res.send(docs);
});
})
I want to build a html form, for getting the login password, but how can I change the MongoClient connexion with the new login password afterwards ?
For example, as long as John logs in, he needs to have READ rights to be able to call the getEvaluations web service.
Do I have to always make the connexion with no user , then only check the user session inside each of the web services ?
Or Is it possible to change the pool connexion , each time a mongoDb user logs in ( he might only have READ rights)?
Something like this ?
MongoClient.connect("mongodb://john:passwd#localhost:27017", function(err, client) {
if (err) throw err;
db = client.db('myDb');
// Start the application after the database connection is ready
app.listen(3000);
console.log("Listening on port 3000");
});
For example, let's say "John" with the password "passwd" wants to log in, how could I do ? Is there any standard code somewhere using a connexion pool ?
I also use express-session but dont understand how to use it with the connexion pool.
I can't find a mongoDb auth code using the connexion pool. There are some moogoose examples but, it is not the same .
This is the idea I've got :
The user logs in via HTTP, then if the login is ok, so a session is created, but how could i check afterwards if the user only has READ rights during all of his session ?
This is a pseudo code, from a moongoose example, but this is not what i need, i'm using mongoClient.
app.get('/getAuth', function(req, res) {
var email = req.param('email');
var password = req.param('password');
//authenticate input against database
UserSchema.statics.authenticate = function (email, password, callback) {
User.findOne({ email: email })
.exec(function (err, user) {
if (err) {
return callback(err)
} else if (!user) {
var err = new Error('User not found.');
err.status = 401;
return callback(err);
}
bcrypt.compare(password, user.password, function (err, result) {
if (result === true) {
return callback(null, user);
} else {
return callback();
}
})
});
}
})
If somebody has an example code, i would really appreciate it, I don't use moogoose , but mongoClient Thank you.
or maybe, this auth web service would be better :
app.get('/getAuth', function(req, res) {
var user = req.param('user');
var password = req.param('password');
db.authenticate(user, password, function(err, res) {
// callback
});
})
but then, what should I do with the connexion pool ? How could I reload it ?
I can't understand how to do.
EDIT 1: This is what I got for now : I've built a getAuth web service, with a pool connexion starting inside of it, is it the right way to do ?
var db;
app.get('/getAuth', function(req, res) {
var user = "jose";
var password = "passwd";
MongoClient.connect("mongodb://"+user+":"+password+"#localhost:27017", function(err, client) {
if (err) {console.log(err)}
else{
db = client.db('myDb');
// Start the application after the database connection is ready
console.log("correct log in");
}
});
})
how could I use a session now?
EDIT 2 : Seems to be working code with express-session using this doc :
https://codeforgeek.com/manage-session-using-node-js-express-4/
//use sessions for tracking logins
app.use(session({secret: 'ssshhhhh',saveUninitialized: true,resave: true}));
sess ={};
// ---------------------------------------------------------------------AUTH USER
app.get('/getAuth', function(req, res) {
var user = "jose";
var password = "passwd";
MongoClient.connect("mongodb://"+user+":"+password+"#localhost:27017", function(err, client) {
if (err) {console.log(err)}
else{
db = client.db('myDb');
sess = req.session;
/*
* Here we have assign the 'session' to 'sess'.
* Now we can create any number of session variable we want.
* in PHP we do as $_SESSION['var name'].
* Here we do like this.
*/
sess.email = "jose.golo#gmail.com"; // equivalent to $_SESSION['email'] in PHP.
sess.username = "jose"; // equivalent to $_SESSION['username'] in PHP.
res.end('done');
}
});
})
Then, in my getEvaluations web service, i 'm checking if sess contains an email, otherwize, it means that he's not logged in, so he can't access the evaluations node.js web service :
app.post('/getEvaluations', function(req, res) {
if (!sess.hasOwnProperty("email") ){
console.log('not logged in ');
}else{
db.collection('evaluations').find().toArray(function(err, docs) {
if (err) throw err;
res.send(docs);
});
}
})
If you have a better solution, please let me know . This app is front end Ionic 1 app .
Question :
Is it a normal behavior :
I get authenticated inside of firefox, so i can access my evaluations.
Now, I go to chrome, on the same computer , i don't have to authenticate to view the evaluations ?
Is it because it is a user Id based session ?
This is how I would do it:
Let the user log in via a HTTP form using the POST method.
Try and find the user's document inside your mongo database (eg. by username). This will result in a user object containing your user's credentials and whatever rights he/she has.
Compare the credentials with the input provided by the log-in form (which is contained within the request object's body).
If the given input and previously stored credentials are equal to each other you can create a session and assign the user object to it.
Check if there's a session object inside the request when the user navigates to '/getEvaluations'. This session object will contain user object of the currently logged in user.
Respond with your Evaluations object if and only if the user has the appropriate
rights.
You could try out authentication middleware like Passport.js. These will handle the sessions and its user data object for you. Here's an example using the local strategy: https://github.com/passport/express-4.x-local-example
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({
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.
I'm using sails 0.10.4 and stumbled with one pretty annoying bug. When user logs in I write his data into the req.session.user then in policies I can retrieve his data such as his role, password etc. But the req.session.user becomes undefined when I go out of the login action. Do you have any ideas how to handle this? Here's the code:
api/controllers/User.js :
module.exports = {
login: function (req, res) {
Users.findOneByEmail(req.param('email'))
.exec(function (err, user) {
if ((err) || (!user)) {
res.send({
error: 'User not found'
});
return;
}
if (!passwordHash.verify(req.param('password'), user.password)) {
res.send({
error: 'Incorrect passwpord'
});
return;
}
req.session.user = user;//I write user into the session
res.send({
user: user
});
});
}
}
api/policies/isLoggedIn.js
module.exports = function (req, res, next) {
if (req.headers.authentication) {
var credentials = JSON.parse(req.headers.authentication);
if(req.session.user.login === credentials.login)//User doesn't exist in session
return next();
}
}
In a testing environment , this issue can happen when testing with Supertest and not defining an agent
var agent = request.agent(app);
agent.post('/api/login',{email:'foo#bar.com',password:'foobar})
.end(function(err,res){...; done();});
It is the correct way to work with sessions, simply using request.post would not work as it would reinit the session variable as soon as the response is sent, even if we are chaining requests inside the same test.
Learnt it the hard way, so I hope it can help some lost developper.
I am using express.basicAuth to login, right now without any database. Here's my auth.js file:
var express = require('express');
var admins = {
admin#foo.com': { password: 'Passw0rd1' },
michael#foo.com': { password: 'Passw0rd2' },
};
var handler = express.basicAuth(function(user, pass) {
var u = admins[user];
if (!u) return false;
return u.password === pass;
});
module.exports = function(req, res, next) {
return handler(req, res, function(err) {
req.admin = req.remoteUser;
res.local('admin', req.remoteUser);
return next(err);
});
};
I have noticed that my service is under brute force attacks, same ip trying to login by enumerating the possibilities.
To discourage future attacks, I would like to add the ability to redirect user to Recaptcha service after 3 failed login attempts.
I used the node-recaptcha module successfully but I don't know how to integrate it with basicAuth and to keep track of the ips.
Any ideas ?