I'm making a webapp that uses Socket.io to pass information between the server and the client, one example being login information. The documentation for passport.authenticate says to use it like so:
app.post('/login', passport.authenticate('local', { successRedirect: '/',
failureRedirect: '/login' }));
However, my webapp is using Polymer client-side routing, so the only route my index.js has is this:
app.get('*', function (req, res) {
res.sendFile('./public/index.html', {root: '.'});
});
Instead, I'd like to do something like this:
io.on('connection', function(socket){
socket.on('login', function(data){
passport.authenticate('local', data);
});
});
However, this doesn't work as the authenticate function doesn't even get called right now. Is there a way to make passport work in such a scenario?
You can try something like below .
In your routes define and require the socket module, so you have access to use it in routes.
var express = require('express');
var app = express();
var server = http.createServer(app);
var io = require('socket.io').listen(server);
var router = express.Router();
var passport = require('passport');
io.on('connection', function(socket){
socket.on('login', function(data){
// call the routes
router.post('/login', function(request, response, next) {
passport.authenticate('local', function(err, user, info) {
if (err) {
// return next(err);
socket.emit('loginResult', { success: false });
}
if (!user) {
var message = "Invalid credentials";
socket.emit('loginResult', { success: false , message: message});
}
request.logIn(user, function (err) {
if (err) {
socket.emit('loginResult', { success: false });
}
// if want to save user in session
request.session.user = user;
// after success code
socket.emit('loginResult', { success: true , user : user});
});
})(request, response, next);
});
});
});
Hope this helps.
You can define your custom callback with passport.authenticate(). I have given a example below, you might wanna try that. Go here for more info.
io.on('connection', function(socket){
socket.on('login', function(data){
var req = {}
req.body = data
passport.authenticate('local', function(err, user, info) {
if (err) {
socket.emit('login', { success: false });
}
if (!user) {
socket.emit('login', { success: false });
}
// Set session
req.logIn(user, function(err) {
if (err) {
socket.emit('login', { success: false });
}
socket.emit('login', { success: true });
});
});
});
Update: Problem with previous code was, when using custom callbacks in passport authenticate it uses req object from the closure, which in this case was undefined as it was not in the router. I think, now that you can provide enough authentication data through req.body it should work.
Related
I know this question was asked many times in stack over flow. I tried every accepted answers but can't my local strategy into function. Here is my code
var express = require('express');
var app = express();
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
app.use(cookieParser()); // read cookies (needed for auth)
app.use(bodyParser.json({limit: '50mb'}));
app.use(bodyParser.urlencoded({limit: '50mb', extended: true}));
app.set('trust proxy', 1); // trust first proxy
app.use(session({
secret: '564sdf4as564f56a7s765s4afjkgadxjkbadksj',
resave: true,
saveUninitialized: true,
cookie: { secure: true }
}));
app.use(passport.initialize());
app.use(passport.session());
passport.use(new LocalStrategy({
usernameField:'userName',
passwordField:'password',
passReqToCallback : true
},function(request, userName, password, done) {
console.log(request);
UserAccount.findOne({'userName': userName} , function(err, user) {
if (err) return done(err);
if (!user) return done(null, false, 'Incorrect username.' );
user.verifyPassword(password, function(err, isMatch) {
if (isMatch) {
return done(null, user);
} else {
return done(null, false, 'Incorrect password.');
}
});
});
}));
passport.serializeUser(function(user, done) {
console.log('Serialize user called');
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
console.log('Deserialize user called');
UserAccount.findById(id, function(err, user) {
done(err, user);
});
});
Then I created a router like
var router = express.Router();
require('./controllers/user')(router,passport);
app.use('/api',router);
Then in my user controller I created signIn function like
app.post('/signIn',function (request,response,next){
var variables = request.body;
console.log(variables);
passport.authenticate('local', function(error, user, info) {
console.log(user);
if (error) { console.log(error); return next(err); }
if (!user) { return response.redirect('/login'); }
response.logIn(user, function(err) {
if (err) { return next(err); }
return response.redirect('/users/' + user.username);
});
})(request, response, next);
});
Then I send a request from "Postman"
{
"userName":"karthik#abc.com",
"password":"qwerty"
}
My mongodb userName and password fields are same.
In my db there is an account with this user name and password. But every time it return 'user' as 'false' inside authenticate. I tried to console my request inside local strategy but it never gets called. I don't understand What I done wrong here? Can some one help to solve this? Thank you very much.
You should name local strategy and use it in authenticate.
Use like this passport.use('local-strategy',new LocalStrategy({});
and like passport.authenticate('local-strategy');
If I pass passport.authenticate("local") as middleware into my route, it executes. But this way I do not have access to res so I can send a message back to my front end. However, if I attempt to call it in the route callback function, it is not firing.
router.post("/login", function(req, res, next) {
passport.authenticate("local", function(err, user, info) {
console.log("Unreached"); // This is not logging
});
})
Here is my passport.use inside app.js
passport.use(new LocalStrategy({
usernameField: "portalId"
}, function(portalId, enteredPassword, done) {
var params = {
TableName: "MyTableName",
KeyConditionExpression : "PortalID = :portalID",
ExpressionAttributeValues : {
":portalID" : Number(portalId)
}
}
docClient.query(params, function(err, user) {
if (err) throw err;
let realPassword = user.Items[0].password;
bcrypt.compare(enteredPassword, realPassword, function(err, res) {
if (err) throw err;
if (res) {
return done(null, user);
}
if (!res) {
return done(null, false, { message: "Invalid Credentials" });
}
})
})
}));
Saw in some other post a snippet of code using the custom callback and he had (req, res, next) right after the passport.authenticate function. I added this and my code was being fired now.
i am building a site that as two url ('/','/admin') session are conflicting
here is my app.js session code
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(session({
secret: "JHGF>,./?;;LJ8#$?,KL:>>>,,KJJJDHE",
resave: true,
saveUninitialized: true
}));
app.use(flash());
app.use(passport.initialize());
app.use(passport.session());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', index);
app.use('/admin', admin);
please how can i fix this?
here is index.js
var express = require('express');
var User = require('../models/user');
var Admin = require('../models/admin');
var Pandingpay = require('../models/pandingpay');
var Confirmpay = require('../models/confirmpay');
var passport = require('passport');
var moment = require('moment');
var router = express.Router();
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) {
next();
} else {
req.flash("info", "You must be logged in to see this page.");
res.redirect("/user/login");
}
};
function Authenticated(req, res, next) {
if (req.isAuthenticated()) {
res.redirect('/user/dashboard/');
}else {
next();
}
};
router.use(function(req, res, next){
res.locals.currentUser = req.user;
res.locals.errors = req.flash("error");
res.locals.infos = req.flash("info");
next();
});
/* GET home page. */
router.get('/', function(req, res) {
res.render('index',{
title: 'Home'
});
});
router.post('/login', function(req, res, next) {
passport.authenticate('user-local', {failureFlash:true}, function(err, user, info) {
if(!req.body.password || !req.body.username){
req.flash("error", "Please enter your username and password");
return res.redirect("/login");
}
if (err) { return next(err); }
if (!user) {
req.flash("error", "Sorry username or password is invalied!");
return res.redirect('/login');
}
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/dashboard');
});
})(req, res, next);
});
and here is my admin.js
var express = require('express');
var User = require('../models/user');
var Admin = require('../models/admin');
var Pandingpay = require('../models/pandingpay');
var Confirmpay = require('../models/confirmpay');
var passport = require('passport');
var moment = require('moment');
var routeradmin = express.Router();
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) {
next();
} else {
req.flash("info", "You must be logged in to see this page.");
res.redirect("/admin/login");
}
};
routeradmin.use(function(req, res, next){
res.locals.currentUser = req.user;
res.locals.errors = req.flash("error");
res.locals.infos = req.flash("info");
next();
});
/* GET home page. */
routeradmin.get('/login', function(req, res) {
res.render('adminlogin');
});
routeradmin.post('/login', function(req, res, next) {
passport.authenticate('admin-local', {failureFlash:true}, function(err, user, info) {
if(!req.body.password || !req.body.username){
req.flash("error", "Please enter your username and password");
return res.redirect("/admin/login");
}
if (err) { return next(err); }
if (!user) {
req.flash("error", "Sorry username or password is invalied!");
return res.redirect('/admin/login');
}
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/admin/allusers/' + user.username);
});
})(req, res, next);
});
what i mean by conflicting is that the when an admin login instead of creating a new session for admin it uses the session of an already login useruser
If you want two separate session objects, one for regular usage and one for admin usage with no overlap between them, then you have to do two separate app.use('/path1', session(...)) and app.use('/path2', session(...)) statements so you have two separate session managers for different paths and make sure each has a different cookie name (using the name parameter to the session() options). And, then you have to design your URLs to be sub-paths of those so they get the right path.
Usually, people only use one session and then just keep a flag in the session whether it's admin login or not and you can check that flag when needed.
I think it is possible that the flash widget is somewhere in the shared app? Rather than split out into the different paths?
Does that make sense
I have installed Jasmine CLI globally using npm install -g jasmine
I'm trying to test multiple http requests at once using test suite below, multiple calls per each requests were sent (seeing output of console.log() but nothing returned so the test was failure, please guide me is this possible to do so ? and how to do this ?
index.js
var app = require('express')();
var request = require('request');
app.get('/', function(req, res) {
console.log('GET /');
res.status(200);
res.send('Hello World');
});
app.listen(3000);
spec/multipleRequestSpec.js
var request = require('request');
var async = require('async');
describe('express application', function() {
var baseUrl = 'http://localhost:3000';
var statusCode = [0, 0];
var b = ['', ''];
beforeEach(function(done) {
async.parallel([
function() {
request.get(baseUrl, function(err, res, body) {
statusCode[0] = res.statusCode;
b[0] = body;
})
}
,
function() {
request.post(baseUrl, function(err, res, body) {
statusCode[1] = res.statusCode;
b[1] = body;
})
}
], done());
});
it('should return 200', function() {
expect(statusCode[0]).toBe(200);
});
it('should return hello world', function() {
expect(b[0]).toEqual('Hello World');
});
it('should return error 404', function() {
expect(statusCode[1]).toBe(404);
});
});
Edited
When testing only one request I place done() inside the request() it works just fine, but I quite confuse where to place done() when using async.pararell()
spec/requestSpec.js
var request = require('request');
describe('expresss application', function() {
var baseUrl = 'http://localhost:3000';
var statusCode = 0;
beforeEach(function(done) {
request.get(baseUrl, function(err, res, body) {
statusCode = res.statusCode;
done();
});
});
it('should return 200', function() {
expect(statusCode).toBe(200);
});
});
In describe block you initiate variable body. And you use it in it blocks. But in request.get and in request.post you have callback function with parameter body which is in use instead of your describe body variable.
Change beforeEach to:
beforeEach(function(done) {
async.parallel([
function(callback) {
request.get(baseUrl, function(err, res, reqBody) {
statusCode[0] = res.statusCode;
body[0] = reqBody;
callback();
})
}
,
function(callback) {
request.post(baseUrl, function(err, res, reqBody) {
statusCode[1] = res.statusCode;
body[1] = reqBody;
callback();
})
}
], done);
});
I think that you should also check err param in request callbacks. Because there may be errors which fails/pass your tests.
For api endpoints tests it is more easy to use superagent or supertest instead of request.
I'm trying to build authentication system with ExpressJS and PassportJS. For session store I use Redis. I wanna use Remember Me. Every time when the user signs in and has marked "remember me" check-box, it should automatically sign in by next visit on site. I have downloaded an example app form Github https://github.com/jaredhanson/passport-remember-me and change for my using.
var express = require('express')
, passport = require('passport')
, LocalStrategy = require('passport-local').Strategy
, mongodb = require('mongodb')
, mongoose = require('mongoose')
, bcrypt = require('bcrypt')
, SALT_WORK_FACTOR = 10
, RedisStore = require('connect-redis')(express);
mongoose.connect('localhost', 'test');
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function callback() {
console.log('Connected to DB');
});
// User Schema
var userSchema = mongoose.Schema({
username: { type: String, required: true, unique: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true},
accessToken: { type: String } // Used for Remember Me
});
// Bcrypt middleware
userSchema.pre('save', function(next) {
var user = this;
if(!user.isModified('password')) return next();
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
if(err) return next(err);
bcrypt.hash(user.password, salt, function(err, hash) {
if(err) return next(err);
user.password = hash;
next();
});
});
});
// Password verification
userSchema.methods.comparePassword = function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if(err) return cb(err);
cb(null, isMatch);
});
};
// Remember Me implementation helper method
userSchema.methods.generateRandomToken = function () {
var user = this,
chars = "_!abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890",
token = new Date().getTime() + '_';
for ( var x = 0; x < 16; x++ ) {
var i = Math.floor( Math.random() * 62 );
token += chars.charAt( i );
}
return token;
};
// Seed a user
var User = mongoose.model('User', userSchema);
var usr = new User({ username: 'bob', email: 'bob#example.com', password: 'secret' });
usr.save(function(err) {
if(err) {
console.log(err);
} else {
console.log('user: ' + usr.username + " saved.");
}
});
// Passport session setup.
// To support persistent login sessions, Passport needs to be able to
// serialize users into and deserialize users out of the session. Typically,
// this will be as simple as storing the user ID when serializing, and finding
// the user by ID when deserializing.
//
// Both serializer and deserializer edited for Remember Me functionality
passport.serializeUser(function(user, done) {
var createAccessToken = function () {
var token = user.generateRandomToken();
User.findOne( { accessToken: token }, function (err, existingUser) {
if (err) { return done( err ); }
if (existingUser) {
createAccessToken(); // Run the function again - the token has to be unique!
} else {
user.set('accessToken', token);
user.save( function (err) {
if (err) return done(err);
return done(null, user.get('accessToken'));
})
}
});
};
if ( user._id ) {
createAccessToken();
}
});
passport.deserializeUser(function(token, done) {
User.findOne( {accessToken: token } , function (err, user) {
done(err, user);
});
});
// Use the LocalStrategy within Passport.
// Strategies in passport require a `verify` function, which accept
// credentials (in this case, a username and password), and invoke a callback
// with a user object. In the real world, this would query a database;
// however, in this example we are using a baked-in set of users.
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: 'Unknown user ' + username }); }
user.comparePassword(password, function(err, isMatch) {
if (err) return done(err);
if(isMatch) {
return done(null, user);
} else {
return done(null, false, { message: 'Invalid password' });
}
});
});
}));
var app = express();
// configure Express
app.configure(function() {
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.engine('ejs', require('ejs-locals'));
app.use(express.logger());
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.session({
store: new RedisStore({ host: '127.0.0.1', port: 6379, prefix: 'chs-sess' }),
secret: '4Md97L1bL4r42SPn7076j1FwZvAiqube',
maxAge: new Date(Date.now() + 3600000)
}));
// Remember Me middleware
app.use( function (req, res, next) {
if ( req.method == 'POST' && req.url == '/login' ) {
if ( req.body.rememberme ) {
req.session.cookie.maxAge = 2592000000; // 30*24*60*60*1000 Rememeber 'me' for 30 days
} else {
req.session.cookie.expires = false;
}
}
next();
});
// Initialize Passport! Also use passport.session() middleware, to support
// persistent login sessions (recommended).
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(express.static(__dirname + '/../../public'));
});
app.get('/', function(req, res){
res.render('index', { user: req.user });
});
app.get('/account', ensureAuthenticated, function(req, res){
res.render('account', { user: req.user });
});
app.get('/login', function(req, res){
res.render('login', { user: req.user, message: req.session.messages });
});
// POST /login
// Use passport.authenticate() as route middleware to authenticate the
// request. If authentication fails, the user will be redirected back to the
// login page. Otherwise, the primary route function function will be called,
// which, in this example, will redirect the user to the home page.
//
// curl -v -d "username=bob&password=secret" http://127.0.0.1:3000/login
//
/***** This version has a problem with flash messages
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login', failureFlash: true }),
function(req, res) {
res.redirect('/');
});
*/
// POST /login
// This is an alternative implementation that uses a custom callback to
// acheive the same functionality.
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err) }
if (!user) {
req.session.messages = [info.message];
return res.redirect('/login')
}
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/');
});
})(req, res, next);
});
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
app.listen(3000, function() {
console.log('Express server listening on port 3000');
});
// Simple route middleware to ensure user is authenticated.
// Use this route middleware on any resource that needs to be protected. If
// the request is authenticated (typically via a persistent login session),
// the request will proceed. Otherwise, the user will be redirected to the
// login page.
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/login')
}
My app doesn't work with Remember Me, every time when I close the browser, I have to sign-in again. I don't know, what I have done wrong.
My second question is, how doe Remember Me works as usual? I have some idea but not exactly sure.