Policy evaluation not working in sails.js with passport - passport.js

I've created a small API module, to have authenticated access for certain controllers using JWT.
Using local authentication to get the token, and JWT for subsequent end points
However the policies are not getting evaluated, it looks like middleware is not getting executed and sails is not evaluating the policy isSubscriber.js in the api/policies folder.
Here are the relevant code snippets:
/*
* Passport login strategy.
*/
const { JsonWebTokenError } = require('jsonwebtoken');
var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
JwtStrategy = require ('passport-jwt').Strategy,
ExtractJwt = require ('passport-jwt').ExtractJwt,
md5 = require('md5');
passport.serializeUser (function(user, done){
done (null, user.id);
});
passport.deserializeUser(function (id, done){
Subscriber.findOne ({id: id}, function (err, user){
done(err, user);
})
});
var opts = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secrtOrKey : 'xxxxx',
issuer: 'xxxxxx',
audience: 'xxxxx'
};
passport.use (new JwtStrategy(opts, function (jwt_payload, done){
sails.log ("Authenticating with JWT strategy");
Subscriber.findOne ({id: jwt_payload.id}, function (err, subscriber){
if (err){
return done (err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
}
});
}));
passport.use (new LocalStrategy({
usernameField: 'phone',
passwordField: 'password'
},
function (phone, password, done) {
sails.log ("Locating subscriber: " + phone + " Password: " + password);
Subscriber.findOne ({phone: phone}, function (err, subscriber){
if (err) { return done (err);}
sails.log ("Subscriber : " , subscriber);
if (!subscriber) {
sails.log ('User with phone ' + phone + ' not found!');
return done (null, false, {message : 'Subscriber not found.'});
}
sails.log ("Checking password: ");
if (md5(password) != subscriber.password_hash) {
sails.log ('Password mismatch for user ' + phone);
return done (null, false, {message: 'Invalid password'});
}
sails.log ("Successfully logged in");
return done(null, subscriber, {
message: 'Logged in succesfully.'
});
})
}
));
Middleware section in http.js
middleware: {
passportInit: require('passport').initialize(),
passportSession: require('passport').session(),
order: [
'cookieParser',
'session',
'passportInit',
'passportSession',
'bodyParser',
'compress',
'poweredBy',
'router',
'www',
'favicon',
]
policies:
module.exports.policies = {
'*': ['isSubscriber'],
'AuthController': {
'login': true // Always allow access to the user login action
}
};
isSubscriber.js:
module.exports = function (req, res, next){
sails.log ("Authenticating using jwt");
passport.authenticate ('jwt', function (error, user, info){
if (error) return res.serverError (error);
if (!user) {
return res.status(403).send ("Failed to authenticate");
}
req.user = user;
sails.log ("User authenticated, user record: " , req.user);
next();
})(req, res);
};

Related

Server returning "Error: Failed to serialize user into session" when attempting to use register user using Passport.js and AJAX

Server returning "Error: Failed to serialize user into session" when attempting to use register user using Passport.js and AJAX. The code works when not using AJAX from the registration page, but I want to have a popup that processes the registration too.
passport.js
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var User = require('../models/user');
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
done(err, user);
});
});
passport.use('local.popsignup', new LocalStrategy({
usernameField: 'popUsername',
passwordField: 'popPassword',
passReqToCallback: true
}, (req, email, password, done) => {
User.findOne({'email':email}, (err, user) => {
if(err){
return done(err);
}
if(user){
return done(null, false, req.flash('error', 'Email already exists, please login.'))
}
var newUser = new User();
newUser.email = email;
newUser.password = newUser.encryptPassword(password);
newUser.preferences = preferences;
newUser.save((err) => {
return done(null, newUser);
});
});
}));
user router
module.exports = (app, passport) => {
app.post('/popover', function(req, res, next) {
passport.authenticate('local.popsignup', function(err, user, info) {
if (user !== undefined) {loggedIn = true} else {loggedIn = false};
var errors = req.flash('error')
req.login(user, function(err) {
if (err) return next(err);
res.status(200).send({errors: errors, loggedIn: loggedIn});
});
})(req, res, next);
});
}
view ajax js
$.ajax({
url: "/popover",
type: "POST",
data: $('#popform-tag').serialize(),
success: function(data){
if(data.loggedIn == false) {
$("#email_alert").text(data.errors[0]).show();
} else {
$('#popover').modal('hide')
localStorage.setItem('preferences', 1);
}
//console.log(data);
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
console.log("Status: " + textStatus);
console.log("Error: " + errorThrown);
}
})
Before adding ajax, send some parameters via PostMan to check if your API request-response is working as expected. If yes, only then use Ajax based on API type (post, put etc..)
Also instead of serialize() try using Ajax in this way -
var data = {
someKey: 'someValue'
}
$.ajax({
url: '/popover', // route path
type: 'POST',
data: JSON.stringify(data),
headers: {
'Content-Type': "application/json",
},
'success': function (data, textStatus, request) {
// do something
},
'error': function (err) {
// do some error handling
}
});

Nuxt auth with passportjs?

How use nuxt auth Module (front-end) with passport-local using JWT (back-end express) ?
defining jwt strategy for verify jwt token (express)
var JwtStrategy = require('passport-jwt').Strategy,
ExtractJwt = require('passport-jwt').ExtractJwt;
var opts = {}
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = 'secret';
opts.issuer = 'accounts.examplesoft.com';
opts.audience = 'yoursite.net';
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findOne({id: jwt_payload.sub}, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
// or you could create a new account
}
});
}));
defining local strategy for verify username nad password (express)
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); }
if (!user.verifyPassword(password)) { return done(null, false); }
return done(null, user);
});
}
));
code for issuing token after verifying username and password (expresss)
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login' }), //need to update from nuxt auth.
function(req, res) {
res.redirect('/');
});
nuxt auth local strategy consume username and passsword returns a JWT token (nuxt)
this.$auth.loginWith('local', {
data: {
username: 'your_username',
password: 'your_password'
}
})
It can work independently how do i combine these ?
code for express
Create passport strategies
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const JwtStrategy = require('passport-jwt').Strategy;
passport.use(
new LocalStrategy(
{
usernameField: 'username',
passwordField: 'password'
},
function(username, password, done) {
users.findOne({ email: username }, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, { error: 'Invalid username' });
}
if (!user.checkPassword(password)) {
return done(null, false, { error: 'invalid password' });
}
const info = { scope: '*' };
done(null, user, info);
});
}
)
);
const opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = 'JWT_SECRET_OR_KEY';
passport.use(
new JwtStrategy(opts, function(payload, done) {
users.findById(payload, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
}
return done(null, false);
});
})
);
use passport strategies
const express = require('express');
const passport = require('passport');
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(passport.initialize()); // Used to initialize passport
// Routes
app.post(
'/login',
passport.authenticate('local', { session: false }),
function(req, res) {
const token = jwt.sign(req.user.userId, 'JWT_SECRET_OR_KEY');
return res.json({ token });
}
);
app.get(
'/me',
passport.authenticate(['jwt', 'bearer'], { session: false }),
function(req, res, next) {
const { userId } = req.user;
users.findOne({ _id: userId }, (err, data) => {
if (err) {
res.status(500).send(err);
} else if (data) {
const userData = data;
res.status(200).send(userData);
} else {
res.status(500).send('invalid token');
}
});
}
);
configuration for nuxt
inside nuxt.config.js
auth: {
resetOnError: true,
redirect: {
login: '/login', // User will be redirected to this path if login is required.
home: '/app/dashboard', // User will be redirect to this path after login. (rewriteRedirects will rewrite this path)
logout: '/login', // User will be redirected to this path if after logout, current route is protected.
user: '/user/profile',
callback: '/callback // User will be redirect to this path by the identity provider after login. (Should match configured Allowed Callback URLs (or similar setting) in your app/client with the identity provider)
},
strategies: {
local: {
endpoints: {
login: {
url: '/login',
method: 'post',
propertyName: 'token'
},
logout: false,
user: {
url: '/me',
method: 'GET',
propertyName: false
}
},
tokenRequired: true,
tokenType: 'Bearer'
}
}
inside Login .vue
this.$auth
.loginWith('local', {
data: {
username: this.user.email,
password: this.user.password
}
})
.catch(err => {
console.error(err );
});

Passport authentication doesnot seems to be working

This is my MiddleWare for Passport.js the issue i am getting here i cant logged in getting no errors, register route is working successfully i am entering the Right Credentials and everything from my view is great but i am not redirecting to anywhere i tried i am getting http:500 error and getting NO Select query where username='username' and password='password'
//Passport config
require('./config/passport')(passport)
//Passport MiddleWare
app.use(passport.initialize());
app.use(passport.session());
This is my passport.js file code
var LocalStrategy = require('passport-local').Strategy;
var User = require('../models/user');
var bcrypt = require('bcryptjs');
module.exports = function (passport) {
passport.use(new LocalStrategy(function (username, password, done) {
models.User.findOne({
where:{username: username}
}.then(function (err,user) {
if(err) console.log(err);
if (!user) {
return done(null, false, {message: 'No user found!'});
}
bcrypt.compare(password, user.password, function (err, isMatch) {
if (err)
console.log(err);
if (isMatch) {
return done(null, user);
} else {
return done(null, false, {message: 'Wrong password.'});
}
});
})
)
}));
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
User.findById(id, function (err, user) {
done(err, user);
});
});
}
this is my login method in route
/*
*Post Login
*/
router.post('/login',function(req,res,next){
passport.authenticate('local',{
successRedirect:'/',
failureRedirect:'/admin/pages',
failureFlash:true
})(req,res,next);
})
This is my user.js
var express=require('express');
var router=express.Router();
var User = require('../models/user');
var models=require('../models');
var passport=require('passport');
var bcrypt=require('bcryptjs');
//Get Register
router.get('/register',function(req,res){
res.render('register',{
title:'Register'
})
});
/*
* Register Post
*/
router.post('/register',function(req,res){
var name=req.body.name;
var email=req.body.email;
var username=req.body.username;
var password=req.body.password;
var password2=req.body.password2;
models.User.findOne({
where:{username:username}
}).then(function(copyuser){
if(copyuser){
console.log('User already exist');
redirect('/users/login');
}
else{
var user={
name: name,
email: email,
username: username,
password: password,
admin: 0
};
// res.json(user);
bcrypt.genSalt(10,function(err,salt){
bcrypt.hash(user.password,salt,function(err,hash){
user.password=hash;
models.User.create(user).then(function(user){
res.json(user);
})
console.log('You are now registered');
res.redirect('/admin/pages')
})
})
}
})
})
/*
* Get Login
*/
router.get('/login',function(req,res){
if(res.locals.user) res.redirect('/');
res.render('login',{
title:'log in'
})
})
/*
*Post Login
*/
router.post('/login',function(req,res,next){
passport.authenticate('local',{
successRedirect:'/',
failureRedirect:'/admin/pages',
failureFlash:true
})(req,res,next);
})
//Exports
module.exports=router;

Failed to serialize user into session when using passport-facebook strategy with passport-local

I am using passport-local and passport-facebook strategies for authentication in sails.js. Authentication with email is working fine. But when user authenticates using facebook, I am getting this error message [Error: Failed to serialize user into session].
Then I tested serializeUser method and it turns out user param is empty in case of facebook. While I also tried to see if verifyHandler is called or not and it is not being called.
Here is my code for the facebook authentication action:
facebook: function (req, res) {
passport.authenticate('facebook', {failureRedirect: '/login', scope: ['email']}, function (err, user) {
if ((err) || (!user)) {
req.session.flash = {
errMsg: 'Email or password mismatch.'
}
return res.redirect('/login');
}
req.logIn(user, function (err) {
if (err) {
console.log(err);
res.view('500');
return;
}
res.redirect('/');
return;
});
})(req, res);
}
And this is the code of passport.js service (api/services/passport.js)
var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
FacebookStrategy = require('passport-facebook').Strategy,
bcrypt = require('bcrypt');
var verifyHandler = function (token, tokenSecret, profile, done) {
console.log('in verifyHandler'); // this line is not being executed.
console.log(profile);
process.nextTick(function () {
User.findOne({uid: profile.id}, function (err, user) {
if (err) {
return done(err);
}
if (user) {
return done(null, user);
} else {
var data = {
provider: profile.provider,
uid: profile.id,
name: profile.displayName
};
if (profile.emails && profile.emails[0] && profile.emails[0].value) {
data.email = profile.emails[0].value;
}
User.create(data, function (err, user) {
return done(err, user);
});
}
});
});
};
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
User.findById(id, function (err, user) {
done(err, user);
});
});
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'password'
},
function (email, password, done) {
User.findOne({email: email}).exec(function (err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, {message: 'Unknown user ' + email});
}
bcrypt.compare(password, user.password, function (err, res) {
if (!res) return done(null, false, {message: 'Invalid Password'});
return done(null, user);
});
});
}
));
passport.use(new FacebookStrategy({
clientID: sails.config.facebook.clientID,
clientSecret: sails.config.facebook.clientSecret,
callbackURL: sails.config.facebook.callbackURL
}, verifyHandler));
And finally (config/passport.js)
var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
FacebookStrategy = require('passport-facebook').Strategy;
module.exports = {
http: {
customMiddleware: function (app) {
app.use(passport.initialize());
app.use(passport.session());
}
}
};
Any thoughts?
Check if user.id is defined and it is string but not ObjectId().
in
passport.serializeUser(function (user, done) {
done(null, user.id);
});

req.session.passport and req.user empty, serializeUser and deserializeUser are never called

I'm using Express (v4.11.2) with Passport in order to support multiple providers (local, facebook, twitter and google) for access to the web app I'm building. As a backend I'm using mysql. For now I have two local strategies: local-signup and local-signin. The issue I'm experiencing is that the req.session.passport and req.user are always empty and that, in fact, serializeUser and deserializeUser are never being called.
Here is the setup of express and passport:
var bodyParser = require('body-parser');
var session = require('express-session');
var MemoryStore = session.MemoryStore;
var _ = require('underscore');
var passport = require('passport');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(session({
key: 'KEY',
secret: 'SECRET331156%^!fafsdaasd',
store: new MemoryStore({reapInterval: 60000 * 10}),
saveUninitialized: true,
resave: false
}));
app.use(passport.initialize());
app.use(passport.session());
require('./config/passport')(passport); // pass passport for configuration
and here is the passport file with authentication strategies:
module.exports = function (passport) {
passport.serializeUser(function (user, done) {
logger.info('SERIALIZE USER');
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
logger.info('DESEIRALIZE USER!');
mysqllib.getConnection(function (err, connection) {
if (err) {
done(err);
}
var sql = "SELECT * FROM users WHERE idusers = ?";
logger.info('sql: ' + sql);
connection.query(sql, [id], function (err, rows) {
connection.release();
var user = {};
user.id = rows[0].idusers;
done(err, user.id);
});
});
});
passport.use('local-signup', new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true // allows us to pass back the entire request to the callback
},
function (req, email, password, done) {
logger.info('CALLING local-signup');
var firstname = req.body.firstname;
var lastname = req.body.lastname;
var role = req.body.role;
mysqllib.getConnection(function (err, connection) {
var sql = "INSERT INTO users VALUES(0, ?, ?, ?, ?, null, ?, 0, null, null, null, null, null, null, 0, 0)";
logger.info('sql: ' + sql);
connection.query(sql, [email, password, firstname, lastname, role], function (err, rows) {
connection.release();
if (err) {
if (err.code == 'ER_DUP_ENTRY') {
logger.info('er_dup_entry');
return done(err);
} else {
logger.info('general err');
return done(err);
}
} else {
logger.info('everything is OK!');
var user = {};
user.id = rows.insertId;
req.session.user_auth = user.id;
return done(null, user);
}
});
});
}));
passport.use(
'local-login',
new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true // allows us to pass back the entire request to the callback
},
function (req, email, password, done) {
mysqllib.getConnection(function (err, connection) {
if (err) {
logger.info('getConnection: ' + err);
return done(err);
}
var sql = "SELECT idusers, first_name, last_name, email, phone, dob, address, role, photo1, photo2, photo3, photo4, phonevalidated, uservalidated FROM users WHERE email = " + connection.escape(email) + " AND password = " + connection.escape(password);
connection.query(sql, function (err, rows) {
connection.release();
if (err) {
logger.error("select user", err);
return done(err);
} else if (rows.length) {
var user = rows[0];
user.id = rows[0].idusers;
return done(null, user);
} else {
logger.warn('Incorrect Login credentials, username: ' + email + ' password: ' + password);
return done(null, false, {message: 'unauthorized'});
}
});
});
})
);
};
and, for last, here is how I'm using the strategies in express routes:
app.post('/login', function (req, res, next) {
passport.authenticate('local-login', function (err, user, info) {
if (err) {
mysend(res, 500, 'Ups. Something broke!');
} else if (info) {
mysend(res, 401, 'unauthorized');
} else {
mysend(res, 200, JSON.stringify(user));
logger.info(req.user);
logger.info(req.session);
}
})(req, res, next);
});
Everything works fine, even in a strategy I can set the value of user's id in the session like this:
req.session.user_id = user.id
and continue to use it manually but I really don't get why serializeUser and deserializeUser aren't being called.
You need to call req.login() in custom callback which will then call serializeUser and set the user object to the session:
app.post('/login', function (req, res, next) {
passport.authenticate('local-login', function (err, user, info) {
if (err) {
mysend(res, 500, 'Ups. Something broke!');
} else if (info) {
mysend(res, 401, 'unauthorized');
} else {
req.login(user, function(err) {
if (err) {
mysend(res, 500, 'Ups.');
} else {
mysend(res, 200, JSON.stringify(user));
}
}
}
})(req, res, next);
});

Resources