I was not able to secure individual routes of my adminpanel using passport.js
User Signup is working. Even when I login into panel its successfully redirectly . But the req.isAuthenticate is always returning false value. Hence, I am not able to access routes inside admin panel
Codes
controller/admin.js
var express = require('express'),
router = express.Router(),
session=require('express-session');
module.exports = function (app) {
app.use('/', router);
};
var passport = require('passport');
var flash = require('connect-flash'),
session = require('express-session');
router.use(session({ secret: 'ilovescotchscotchyscotchscotch' ,saveUninitialized: true, resave: true})); // session secret
router.use(passport.initialize());
router.use(passport.session()); // persistent login sessions
router.use(flash());
router.get('/expoadmin/', function(req, res) {
// render the page and pass in any flash data if it exists
res.render('expoadmin/login', { message: req.flash('loginMessage')});
});
// process the login form
router.post('/expoadmin/login', passport.authenticate('admin-login', {
successRedirect : '/expoadmin/dashboard', // redirect to the secure profile section
failureRedirect : '/expoadmin/', // redirect back to the signup page if there is an error
failureFlash : true // allow flash messages
}));
router.get('/expoadmin/logout', function(req, res){
console.log('logging out');
req.logout();
res.redirect('/expoadmin');
});
router.get('/expoadmin/addadmin', function(req, res) {
// render the page and pass in any flash data if it exists
res.render('expoadmin/signup', { message: req.flash('signupMessage') });
});
// process the signup form
router.post('/expoadmin/signup', passport.authenticate('admin-signup', {
successRedirect : '/expoadmin/admins', // redirect to the secure profile section
failureRedirect : '/expoadmin/addadmin', // redirect back to the signup page if there is an error
failureFlash : true // allow flash messages
}));
var fetch =require('../adminroutes/eventsfetch.js');
router.get('/expoadmin/dashboard', isLoggedIn,
function (req, res, next) { res.render('expoadmin/index',{ layout : 'dashboard'}); });
router.get('/expoadmin/eventsfetch', isLoggedIn, fetch.view );
// route middleware to make sure
function isLoggedIn(req, res, next) {
var ses=req.session;
console.log(req.user);
console.log(session.user);
// if user is authenticated in the session, carry on
if (req.isAuthenticated())
return next();
// if they aren't redirect them to the home page
res.redirect('/expoadmin');
}
passport.js
// config/passport.js
// load all the things we need
var LocalStrategy = require('passport-local').Strategy;
var bcrypt = require('bcrypt-nodejs');
// load up the user model
var Admin = require('../app/models/admin');
// expose this function to our app using module.exports
module.exports = function(passport) {
// =========================================================================
// passport session setup ==================================================
// =========================================================================
// required for persistent login sessions
// passport needs ability to serialize and unserialize users out of session
// used to serialize the user for the session
passport.serializeUser(function(user, done) {
done(null, user._id);
});
// used to deserialize the user
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
// =========================================================================
// Admin LOGIN =============================================================
// =========================================================================
// we are using named strategies since we have one for login and one for signup
// by default, if there was no name, it would just be called 'local'
passport.use('admin-login', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) { // callback with email and password from our form
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
Admin.findOne({ 'local.email' : email }, function(err, user) {
// if there are any errors, return the error before anything else
console.log(user);
if (err)
return done(err);
// if no user is found, return the message
if (!user)
return done(null, false, req.flash('loginMessage', 'No admin found.')); // req.flash is the way to set flashdata using connect-flash
// if the user is found but the password is wrong
console.log(bcrypt.compareSync(password, user.local.password));
if (!bcrypt.compareSync(password, user.local.password))
return done(null, false, req.flash('loginMessage', 'Oops! Wrong password.')); // create the loginMessage and save it to session as flashdata
console.log(user);
// all is well, return successful user
return done(null, user);
});
}));
};
router.post('/expoadmin/login',function(req,res,next){
passport.authenticate('admin-login', function (err, user, info) {
if (err) {
//send error message here
}
// Generate a JSON response reflecting authentication status
if (!user) {
//send if user not found
}
else{
req.logIn(user, function (err,data) {
if (err) {
//some error with serialization
}
//do your stuff with info here
res.redirect('/expoadmin/dashboard')
});
});
}
})(req, res, next);
})
you callback will be received here in (err,user,info)
send final req as
return done(null,false,user)
now check req.isAuthenticated
Related
I am currently trying to set up an admin role in order to access a simple admin page using the following documentation provided via : connect-roles
I ave been banging my head against it for a while and am still lost on how to set a role E.G As of right now am pulling a admin value out of the DB and storing it in a global var for the time being but I have no idea how to use that with connect-roles say to only allow access to my admin page for a specific user.
Can anyone clarify or show an example on how to do this/some guidance as I documentation didn't help me to ensure access to a web page only if the user is an admin?
Ave posted some of the code kinda showing what it looks like at the moment.
Code
var admin = 'Admin';
var mysql = require('mysql');
var connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : '',
database : 'test'
});
var passport = require('passport');
var ConnectRoles = require('connect-roles');
var roles = new ConnectRoles();
var passportLocal = require('passport-local');
app.use(passport.initialize());
app.use(passport.session());
app.use(roles.middleware());
passport.use(new passportLocal.Strategy(function (username, password, done) {
connection.query({
sql : 'SELECT * from `userman_users` WHERE `username`= ?AND`password` = sha1(?)',
timeout : 40000, // 40s
values : [username, password]
}, function (error, results, rows) {
if (results.length > 0) {
response = "Success";
} else {
console.log('Error while performing Query.');
response = "Failed";
}
if (response === "Success") {
done(null, {
id : username
});
} else if (response === "Failed") {
done(null, null);
}
});
})
);
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
done(null, {
id : id
});
});
roles.use(function (req, action) {
if (!req.isAuthenticated()) return action === 'access home page';
})
roles.use(function (req) {
if (req.user.role === 'admin') {
return true;
}
});
app.get('/', redirectToIndexIfLoggedIn, function (req, res) {
res.render('login');
});
app.get('/index', checkLoggedIn, function (req, res) {
res.render('index', {
isAuthenticated : req.isAuthenticated(),
user : req.user
});
});
app.get('/admin', user.can('access admin page'), function (req, res) {
res.render('admin');
});
function checkLoggedIn(req, res, next) {
if (req.isAuthenticated())
return next();
res.redirect('/');
}
this is an example:
var express = require('express');
...
var passport = require('passport');
var LocalStrategy = require('passport-local');
var ConnectRoles = require('connect-roles');
...
var app = express();
//===============PASSPORT=================
// Passport session setup.
passport.serializeUser(function(user, done) {
console.log("serializing " + user.username);
done(null, user);
});
passport.deserializeUser(function(obj, done) {
console.log("deserializing " + obj);
// simulate an admin user
obj.role = obj.username == 'admin' ? 'admin' : 'user';
done(null, obj);
});
...
//===============CONNECTION RULES=================
var user = new ConnectRoles({
failureHandler: function (req, res, action) {
// optional function to customise code that runs when
// user fails authorisation
var accept = req.headers.accept || '';
res.status(403);
if (~accept.indexOf('html')) {
res.render('access-denied', {action: action});
} else {
res.send('Access Denied - You don\'t have permission to: ' + action);
}
}
});
...
app.use(passport.initialize());
app.use(passport.session());
app.use(user.middleware());
//anonymous users can only access the home page
//returning false stops any more rules from being
//considered
user.use(function (req, action) {
if (!req.isAuthenticated()) return action === 'access home page';
});
//users logged can access to public pages
user.use(function(req, action){
if(req.isAuthenticated() && action != 'access private page' && action != 'access admin page')
return true;
});
//moderator users can access private page, but
//they might not be the only ones so we don't return
//false if the user isn't a moderator
user.use('access private page', function (req) {
console.log('access private page');
if (req.user.role === 'moderator') {
return true;
}
});
//admin users can access all pages
user.use(function (req) {
if (req.user.role === 'admin') {
return true;
}
});
...
/* GET home page. */
app.get('/', user.can('access home page'), function(req, res, next) {
res.render('index', { title: 'Express' });
});
//displays our signup page
app.get('/signin', function(req, res){
res.render('signin');
});
//sends the request through our local signup strategy, and if successful takes user to homepage, otherwise returns then to signin page
app.post('/local-reg', passport.authenticate('local-signup', {
successRedirect: '/',
failureRedirect: '/signin'
})
);
//sends the request through our local login/signin strategy, and if successful takes user to homepage, otherwise returns then to signin page
app.post('/login', passport.authenticate('local-signin', {
successRedirect: '/',
failureRedirect: '/signin'
})
);
// Simple route middleware to ensure user is authenticated.
app.use(function(req, res, next) {
if (req.isAuthenticated()) { return next(); }
req.session.error = 'Please sign in!';
res.redirect('/signin');
});
//logs user out of site, deleting them from the session, and returns to homepage
app.get('/logout', function(req, res){
var name = req.user.username;
console.log("LOGGIN OUT " + req.user.username)
req.logout();
res.redirect('/');
req.session.notice = "You have successfully been logged out " + name + "!";
});
app.get('/private', user.can('access private page'), function (req, res) {
res.render('private');
});
app.get('/admin', user.can('access admin page'), function (req, res) {
res.render('admin');
});
app.use('/users', users);
....
module.exports = app;
With connect-rules you define the rules do you want to use (user.use in this case). If you pass an action as first parameter the strategy is only used if the action passed in the function is equal to it. Then you trigger the rules in the routes with user.can passing the action. In this example I define an extra filter strategy to grant access to users that are logged and request routes that are not marked with admin or moderator privileges e.g
/* GET home page. */
app.get('/', user.can('access home page'), function(req, res, next) {
res.render('index', { title: 'Express' });
});
After the user is logged, we need to have another strategy in case the user isn't admin or moderator.
U can use framework like sailsJS and npm module sails-generate-auth
And after setup, use your own middleware to block routes
//allow admin only localhost:PORT/admin at policies.js
'admin': ['passport', 'sessionAuth', 'isAdmin'],
'*': ['passport', 'sessionAuth'],
//isAdmin policy
module.exports = function(req, res, next) {
// User is allowed, proceed to the next policy,
// or if this is the last policy, the controller
if (req.user.role == 'admin') {
return next();
}
// User is not allowed
return res.forbidden('You are not permitted to perform this action.');
};
Using the following logic I was able to have admin functionality based on value within the DB:
app.get('/admin', function (req, res) {
connection.query({
sql : 'SELECT role from `auth_users` WHERE `username`= ?',
timeout : 40000, // 40s
values : [req.user['id']]
}, function (error, results, rows) {
if (results[0]['role'] === "admin") {
admin = (results[0]['role']);
res.render('admin', {
isAuthenticated : req.isAuthenticated(),
user : req.user
});
} else {
admin = "";
res.redirect('/index');
}
})
});
The below custom call back for passport.js doesn't seems to work, no mater what i do.
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, users, info) {
console.log(users);
if (user === false) {
console.log('Failed!');
} else {
res.redirect('/');
}
})(req, res, next);
});
The same if i change it to like below all works as expected.
app.post("/login"
,passport.authenticate('local',{
successRedirect : "/",
failureRedirect : "/login",
})
);
Also I've noticed when using custom callback even the passport.serializeUser and passport.deserializeUser also not getting invoked by passport.js.
Is this any sort of a bug or am i doing something wrong here ??
My Local-Strategy:
passport.use('local-sigin',new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) { // callback with email and password from our form
console.log('Passport Strategy Sign in:');
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({ 'email' : email }, function(err, user) {
// if there are any errors, return the error before anything else
if (err)
return done({status:'ERROR',message:'Something went wrong!'});
// if no user is found, return the message
if (!user)
return done({status:'ERROR',message:'No user found.'}, false);
// if the user is found but the password is wrong
if (!user.validPassword(password))
return done({status:'ERROR',message:'Oops! Wrong password.'}, false);
// all is well, return successful user
return done({status:'OK',message:'Login success.'}, user);
});
}));
I am guessing that by 'doesn't work' you mean to say that the user is never being logged in.
Firstly, your local strategy is named 'local-sigin' however on a POST to '/login' you are invoking the 'local' strategy, which presumably doesn't exist:
passport.use('local', new LocalStrategy({
Change the name of your strategy to be consistent (or vice versa!):
passport.authenticate('local'
Secondly, your 'local' authentication callback has a parameter users (plural) but you are trying to access user (singular) within its body, meaning user is undefined and user === false is false under strict equality:
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
// ^^^^
console.log(user);
if (!user) {
console.log('Failed!');
} else {
res.redirect('/');
}
})(req, res, next);
});
And finally, you are never logging the user in when authentication is successful. Creating a session for a user is not automatic, you must call req#login:
Passport exposes a login() function on req (also aliased as logIn()) that can be used to establish a login session.
Let's add that to your authentication callback:
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
console.log(user);
if (!user) {
console.log('Failed!');
} else {
req.login(user, function (err) {
if(err) {
console.log(err);
return;
}
res.redirect('/');
});
}
})(req, res, next);
});
Take a look at the Passport docs, they explain in a good amount of detail how these processes work and how to implement them.
I would like to save the user object within the request object returned from facebook after successful authentication using passport to use in a different method. The purpose for this is to include in my linkschema all different users who post via the same link.
The flow should be as follows:
facebook authenticate
user object is stored somewhere [HOW DO I DO THIS PART???]
link is clicked, gets routed to post method whose parameters are the link and userid.
(If the link exists, the users are appended to the array of users defined in my linkschema)
/ =====================================
/ / FACEBOOK ROUTES
// =====================================
// route for facebook authentication and login
passport.use(new FacebookStrategy({
clientID: configAuth.facebookAuth.clientID,
clientSecret: configAuth.facebookAuth.clientSecret,
callbackURL: "http://localhost:3000/auth/facebook/callback/"
},
function(accessToken, refreshToken, profile, done) {
UserSchema.AddUnique(profile, accessToken, function(err, user) {
if (err) {
return done(err);
}
return done(null, user);
});
}
));
// Redirect the user to Facebook for authentication. When complete,
// Facebook will redirect the user back to the application at
// /auth/facebook/callback
router.get('/auth/facebook', passport.authenticate('facebook', {
scope: 'email'
}));
// Facebook will redirect the user to this URL after approval. Finish the
// authentication process by attempting to obtain an access token. If
// access was granted, the user will be logged in. Otherwise,
// authentication has failed.
var user = router.get('/auth/facebook/callback',
passport.authenticate('facebook', {
failureRedirect: '/login'
}), function(req, res) {
var user = req.user;
res.redirect('/browse');
return function() {
return user;
}();
});
function user() {
console.log('in here');
console.log(user);
}
router.use(function(err, req, res, next) {
console.log(err)
next(err)
});
router.get('/logout', function(req, res) {
req.logout();
res.redirect('/');
});
Thank you in advance!
Store them outside in an object
var Users = {};
passport.use(new FacebookStrategy({…},
function(accessToken, refreshToken, profile, done) {
UserSchema.AddUnique(profile, accessToken, function(err, user) {
if (err) {
return done(err);
}
// Push the user into that object
Users[user.id] = user;
return done(null, user);
});
}
));
function user() {
console.log(Users) //=> { '5234…': {…} , '5345…': {…} , … }
}
I am trying to make a node.js web application that tells the user to sign in using their gmail.
So I tried to use the instructions over here: http://passportjs.org/guide/google/. I changed the url www.example.com to localhost, then ran the application. It tells me that it can't find User. Here is the whole log: User.findOrCreate({openID: identifier }, function(err, user) {(and then on the next line) ReferenceError: User is not defined.
You need to define "User" by calling it from a model. Create a User model (if you haven't already) and import it as a variable. E.g.
var User = require('/path/to/User');
Sometimes I find it helpful for debugging to log the callback to the console, to see if the desired output is being spit out.
I just implemented one maybe this will help , I'm using Express the routes section is on the bottom.. Remember to set your host in the Google Key, my App has de full url of the AWS Server
var passport = require('passport');
// ====== Passport and OAuth2 API
var GoogleStretegy = require('passport-google-oauth').OAuth2Strategy;
passport.serializeUser(function (user, done) {
done(null, user);});
passport.deserializeUser(function (obj, done){
done(null, obj);});
// Set Passport Initialize and Sessions
app.use(passport.initialize());
app.use(passport.session());
passport.use(new GoogleStretegy({
clientID: CREDENTIALS.google.GOOGLE_CLIENT_ID,
clientSecret: CREDENTIALS.google.GOOGLE_CLIENT_SECRET,
callbackURL:"<host>/oauth2callback"
},
function (req, accessToken, refreshToken, profile, done) {
process.nextTick(function () {
console.log(JSON.stringify(profile));
console.log(req);
var username= profile.emails[0].value.split('#');
User.findOne({email: profile.emails[0].value,username:username[0]}).exec(function (err,user) {
if(!user){
var user = new User({username: username[0]});
user.set('email',profile.emails[0].value);
user.set('FullName',profile.DisplayName);
user.save(function (err) {
if(err){
console.log(err);
profile=null;
return done(null,profile);
}else {
return done(null, profile);
}
});
}else {
return done(null, profile);
}
});
// return done(null, profile);
});
}
));
/// ROUTES !
router.get('/logout', function (req, res) {
req.session.destroy(function () {
// Google log out
req.logout();
res.redirect('/login');
});
});
//Google OAuth2
router.get('/auth/google',passport.authenticate('google', { scope: ['https://www.googleapis.com/auth/userinfo.profile','https://www.googleapis.com/auth/userinfo.email'] }));
router.get('/oauth2callback', passport.authenticate('google', { failureRedirect: '/login' }), function (req, res) {
res.redirect('/');
});
I've got a node app that operates in isolation. Without js on in the client it just operates synchronously, setting cookies with passport.js. When the client is js enabled then the auth is done via a rest route.
All seems fine, except that if I have authenticated (and have cookies set) asynchronously, but then refresh the page (or navigate synchronously to a new one [for whatever reason]), the server sends the response with new cookies that overwrite the old ones and setting the user back to being unauthenticated.
// =============================================================================
// AUTHENTICATE (FIRST LOGIN) ==================================================
// =============================================================================
// process the login form
app.post('/login', passport.authenticate('local-login', {
successRedirect: '/profile', // redirect to the secure profile section
failureRedirect: '/browse?p=0', // redirect back to the signup page if there is an error
failureFlash: true // allow flash messages
}));
app.post('/async/login', function(req, res, next) {
passport.authenticate('local-login', function(err, user, info, status) {
if (err) {
return res.send(err);
}
if (user) {
user.local = null;
return res.send(user);
} else {
return res.send(info);
}
})(req, res, next);
});
// =========================================================================
// passport session setup ==================================================
// =========================================================================
// required for persistent login sessions
// passport needs ability to serialize and unserialize users out of session
// used to serialize the user for the session
passport.serializeUser(function(user, done) {
done(null, user.id);
});
// used to deserialize the user
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
// =========================================================================
// LOCAL LOGIN =============================================================
// =========================================================================
passport.use('local-login', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField: 'username',
passwordField: 'password',
passReqToCallback: true // allows us to pass in the req from our route (lets us check if a user is logged in or not)
},
function(req, email, password, done) {
// asynchronous
process.nextTick(function() {
User.findOne({
'local.email': email
}).populate({
path: 'spots comments currentLocation'
}).exec(function(err, user) {
// if there are any errors, return the error
if (err)
return done(err);
// if no user is found, return the message
if (!user)
return done(null, false, req.flash('loginMessage', 'No user found.'));
if (!user.validPassword(password))
return done(null, false, req.flash('loginMessage', 'Oops! Wrong password.'));
// all is well, return user
else
User.populate(user, {
path: 'spots.bird',
model: 'Bird'
}, function(err, user) {
if (err)
return done(err);
else
User.populate(user, {
path: 'spots.location',
model: 'Location'
}, function(err, user) {
if (err)
return done(err);
else
console.log(util.inspect(user, showHidden = false, depth = 5, colorize = true));
return done(null, user);
});
});
});
});
}));
Node App:
app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(cookieParser());
sessionVars = {
keys: ['<removed>'],
//secureProxy: true, // if you do SSL outside of node
maxAge: 2592000000
};
app.use(session(sessionVars));
app.use(flash());
require('./../modules/auth/passport')(passport);
app.use(passport.initialize());
app.use(passport.session());
Does anyone know why?
UPDATE: The async route doesn't seem to call the serializeUser function (which explains everything!). Does anyone know how to force serialization?
If I try a direct call: passport.serializeUser(user, done) then I need the done parameter, which isn't available in the routes file (although I could pass it through);
Does anybody know if serializeUser is meant to be called as a matter of course with passport strategies?
So I found that if you use the custom form of the authenticate function:
app.get('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/login'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/users/' + user.username);
});
})(req, res, next);
});
it doesn't seem to pass it through the serializeUser function that you provide. The cookies for sessions stay in your browser and change all the time, even when you're logged out as they probably still 'tracking' your behaviour.
If you use the 'black box' version of the function:
app.post('/login',
passport.authenticate('local', { successRedirect: '/',
failureRedirect: '/login' }));
that works because it calls serializeUser automatically.