I am using MEAN Stack and mongoose. Using passport for authentication. I want to get the currently logged in user and display his details. For example if I am logged in and if I go to the profile page I want to get my details. There were only few resources on the internet.
Login.js (passport and routes are here)
var express = require('express');
var router = express.Router();
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var User = require('../models/user');
var ConnectRoles = require('connect-roles');
router.get('/', function(req, res, next) {
res.render('Login', { title: 'Login' });
});
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(
function(username, password, done) {
User.findOne({ username: username }, function(err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false,
{ message: "No user has that username!" });
}
user.checkPassword(password, function(err, isMatch) {
if (err) { return done(err); }
if (isMatch) {
return done(null, user);
} else {
return done(null, false,
{ message: "Invalid password." });
}
});
});
}));
router.use(function(req, res, next) {
res.locals.currentUser = req.user;
res.locals.errors = req.flash("error");
res.locals.infos = req.flash("info");
next();
});
router.post("/", passport.authenticate("/", {
successRedirect: "/SubmitSurvey",
failureRedirect: "/",
session: false,
failureFlash: true
}));
router.get("/AdminDatabase", function(req, res) {
res.render('AdminDatabase', { title: 'AdminDatabase' });
});
router.get('/CreateSurvey', function(req, res, next) {
res.render('CreateSurvey', { title: 'CreateSurvey' });
});
router.get('/SubmitSurvey', function(req, res, next) {
res.render('SubmitSurvey', { title: 'SubmitSurvey' });
});
router.get('/profile', function(req, res, next) {
res.render('profile', { title: 'profile' });
});
router.get("/Logout", function(req, res) {
req.logout();
res.redirect("/");
});
module.exports = router;
How can I do this in nodejs
It's on the req.user object
router.get('/profile', function(req, res, next) {
//here it is
var user = req.user;
//you probably also want to pass this to your view
res.render('profile', { title: 'profile', user: user });
});
passport fills the req.user object with the current user through middlewares pipelines so you can extract it from there.
As Alex said, you may explicitly pass some variable (e.g. your req.user) when calling res.render:
router.get('/profile', function(req, res, next) {
res.render('profile', { title: 'profile', user: req.user });
});
Instead, maybe you'd like some variables (like that req.user) to be accessible in all calls to res.render without bothering to specify them every time? In that case, you can use res.locals, e.g. by adding this middleware:
app.use(function(req, res, next){
res.locals.user = req.user;
next();
});
And finally, for completeness -- in both of the approaches above, the variable user is available for you in the view. For example, if you're using a pug template, you may now do something like:
if user
p Hello, #{user.id}, good to see you!
else
p Please log in...
Related
I'm using passport.js in my app for authentication(local). I have two types of user - User and Admin. Both has passport-local-mongoose plugged in. Authentication for User was working fine. Then I applied the same authentication process to Admin.
It doesn't work neither the User which was working fine just before adding auth code to Admin. Instead of redirecting to Admin dashboard, it shows Unauthorized on the way of registering Admin.
I've found a exactly similar problem like mine here. I tried the solution given there but it gets worse. My whole app get crashed and shows some strategy.authenticate is not a function error.
So, Why this error is showing and how can I authenticate, serialize and deserialize both Admin and User in same app?
This is what I've done so far.
here is my app.js
const passport = require("passport"),
localStrategy = require("passport-local").Strategy;
const User = require("./models/user"),
Admin = require("./models/admin");
passport.use(require("express-session") ({
secret : "Wubba lubba dub dub",
saveUninitialized : false,
resave : false
}));
app.use(flash());
app.use(passport.initialize());
app.use(passport.session());
passport.use("user", new localStrategy(User.authenticate()));
passport.use("admin", new localStrategy(Admin.authenticate()));
passport.serializeUser((user, done) => {
done(null, user);
});
passport.deserializeUser(function(user, done) {
if(user!=null)
done(null,user);
});
app.use(function(req, res, next) {
res.locals.currentUser = req.user;
res.locals.error = req.flash("error");
res.locals.success = req.flash("success");
next();
});
user.js login, logout and sign up routes
router.get("/userLogin", (req, res) => {
res.render("user/userLogin");
});
router.post("/userLogin", passport.authenticate("user", {
successRedirect : "/user/1",
failureRedirect : "/userLogin",
}), (req, res)=> {
});
//user sign up handler
router.get("/signUp", (req, res) => {
res.render("user/userSignup");
});
router.post("/signUp", (req, res) => {
const newUser = new User({
firstName : req.body.firstName,
lastName : req.body.lastName,
username : req.body.username,
email : req.body.email,
gender : req.body.gender,
address : req.body.address,
});
User.register(newUser, req.body.password, (err, user) =>{
if(err) {
return res.render("user/userSignup");
}
passport.authenticate("local")(req, res, ()=> {
res.redirect("/user/1");
});
});
});
admin.js login, logout and signup routes
/admin login handler
router.get("/adminLogin", (req, res) => {
res.render("admin/adminLogin");
});
router.post("/adminLogin", passport.authenticate("admin", {
successRedirect : "/admin",
failureRedirect : "/adminLogin",
}), (req, res)=> {
});
//admin logout handler
router.get("/adminLogout", (req, res) => {
res.redirect("/");
});
// sign up
router.get("/adminSignup", (req, res) => {
res.render("signup");
});
router.post("/adminSignup", (req, res) => {
const newAdmin = new Admin({
username : req.body.username,
email : req.body.email,
});
Admin.register(newAdmin, req.body.password, (err, user) =>{
if(err) {
return res.render("signup");
}
passport.authenticate("local")(req, res, function() {
res.redirect("/admin");
});
});
});
and isLoggedIn middleware
middleware.isLoggedIn = function(req, res, next) {
if(req.isAuthenticated()) {
return next();
}
req.flash("error", "You need to be logged in first");
res.redirect("/");
};
I'm a hobbyist coder, and I can usually solve errors with lots of searches, but this one I can't get it right.
when I hit my logout route it throws an error: Cast to ObjectId failed for value "logout" at path "_id" for model "Spot"
I tried mongoose version 4.7.2, it's not working. I can't imagine why is it associating my logout route with the spot model at all.
my app.js
var express = require("express"),
bodyParser = require("body-parser"),
mongoose = require("mongoose"),
passport = require("passport"),
passportFacebook = require("passport-facebook").Strategy,
User = require("./models/user.js"),
Spot = require("./models/spot.js");
mongoose.connect("mongodb://localhost/biketrialspots", { useNewUrlParser: true });
var app = express();
app.set("view engine","ejs");
app.use(bodyParser.urlencoded({extended:true}));
app.use(express.static('public'));
app.use(require("express-session")({
secret: "some secret",
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(function(req, res, next){
res.locals.currentUser = req.user;
next();
});
passport.use(new passportFacebook({
clientID: "some id",
clientSecret: "some secret",
callbackURL: "somewebsite/auth/facebook/callback",
profileFields: ['id', 'displayName', 'picture.type(large)']
}, function(accessToken, refreshToken, profile, done) {
User.findOrCreate(profile, function(err, user) {
if (err)
{ return done(err); }
done(null, user);
});
}
));
passport.serializeUser(function(user, done) {
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
app.get("/", function(req, res){
Spot.find({}, function(err, spots){
if(err){
console.log(err);
} else{
res.render("index", {spots:spots});
}
});
});
app.get("/new", ensureAuthenticated, function(req, res){
res.render("new");
});
app.post("/", function(req, res){
Spot.create(req.body.spot, function(err, result){
if(err){
console.log(err);
} else{
res.redirect("/");
}
});
});
app.get("/:id", function(req, res){
Spot.findById(req.params.id, function(err, spot){
if(err){
console.log(err);
} else{
res.render("spot", {spot: spot});
}
});
});
// Redirect the user to Facebook for authentication. When complete,
// Facebook will redirect the user back to the application at
// /auth/facebook/callback
app.get('/auth/facebook', passport.authenticate('facebook'));
// 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.
app.get('/auth/facebook/callback',
passport.authenticate('facebook', { successRedirect: '/',
failureRedirect: '/login' }));
app.get("/logout", function(req, res){
req.user.logout();
res.redirect('/');
});
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
return res.redirect('/');
}
app.listen(process.env.PORT, process.env.IP, function(req, res){
console.log("APP HAS STARTED!!!!!");
});
user model
var mongoose = require("mongoose");
var userSchema = new mongoose.Schema({
facebookID:Number,
username:String,
photo:String
});
userSchema.statics.findOrCreate = function findOrCreate(profile, cb){
var userObj = new this();
this.findOne({facebookID : profile.id},function(err,result){
if(!result){
userObj.facebookID = profile.id;
userObj.username = profile.displayName;
userObj.photo = profile.photos[0].value;
userObj.save(cb);
} else{
cb(err,result);
}
});
};
module.exports = mongoose.model("User", userSchema);
Thank you
Because app.get("/:id", ...) is written before app.get("/logout", ...) in your code, I guess the request handler of /:id would be called when you get /logout. Then, req.params.id becomes "logout" and the error is thrown by Spot.findById().
How about trying to write app.get("/logout", ...) before app.get("/:id", ...)?
I am trying to use passport authentication with a local strategy but the authentication fails every time and doesn't move on to the local strategy.
I have added a couple console logs to see where the code is derailed but nothing at all is logged.
users.js(router)
var express = require('express');
var router = express.Router();
var User = require('../models/user');
var multer=require('multer');
var passport=require('passport');
var LocalStrategy=require('passport-local').Strategy;
/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('respond with a resource');
});
router.get('/register', function(req, res, next) {
res.render('register',{
'title':'Register'
});
});
router.get('/login', function(req, res, next) {
res.render('login',{
'title':'Login'
});
});
passport.use(new LocalStrategy(
function(username,password,done){
console.log('words');
User.getUserByUsername(username,function(err,user){
if (err) throw err;
if(!user){
console.log('Unknown user');
return done(null,false);
}
});
}
));
router.post('/login',passport.authenticate('local',{failureRedirect:'/users/register'}),function(req,res){
console.log('Authentication succesful');
req.flash('success','You are logged in');
res.redirect('/');
});
module.exports = router;
Did you set the session and added passport to the router? I don't know if setting the initialize and session method in two different spots will work.
This is how I made it work: All set in in a router
let express = require('express');
let session = require('express-session');
let passport = require('passport');
let LocalStrategy = require('passport-local').Strategy;
let router = express.Router();
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.' });
}
user.comparePassword(password, function (err, isMatch) {
if (err) { return done(err); }
if(!isMatch){
return done(null, false, { message: 'Incorrect password.' });
} else {
return done(null, user);
}
});
});
}
));
passport.serializeUser(function(user, done) {
done(null, {email: user.email, roles : user.roles});
});
passport.deserializeUser(function(session, done) {
User.findOne({email: session.email}, function(err, user) {
done(err, user);
});
});
router.use(session({ secret: 'my super secret',name: 'my-id', resave: false, saveUninitialized: false }));
router.use(passport.initialize());
router.use(passport.session());
Furthermore if this ever goes into production you need a different session handler than express-session like MongoStore or Redis
The routes
/* GET home page. */
router.get('/', require('connect-ensure-login').ensureLoggedIn('login'), function (req, res, next) {
if (req.user) {
res.render('index');
} else {
res.redirect('/login');
}
});
router.get('/login', function (req, res, next) {
res.render('login');
});
router.post('/login', passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login'
}));
router.get('/logout', function (req, res) {
req.logout();
res.render('logout');
});
I have the following working code to authenticate through the passport-local strategy:
app.post('/api/login', passport.authenticate('local-login', {
successRedirect : '/api/login/success',
failureRedirect : '/api/login/error',
failureFlash : true
}));
app.get('/api/login/error', function(req, res) {
res.send(401, {error: req.flash('loginMessage')});
});
app.get('/api/login/success', function(req, res) {
res.send(200, {user: req.user});
});
However, ideally I want to handle the errors and success messages from one express route, and not redirect to two extra routes.
Is this possible? I tried using a 'custom callback' but that seemed to error out on serializing users for some reason.
You can use custom callback, such as:
passport.authenticate('local', function (err, account) {
req.logIn(account, function() {
res.status(err ? 500 : 200).send(err ? err : account);
});
})(this.req, this.res, this.next);
In err object you can find all needed errors, which was appeared at authentication.
Are you using Mongoose?
Try adding this to your server.js/index.js
var User = mongoose.model('User');
passport.use(new LocalStrategy(User.authenticate()));
passport.use(User.createStrategy());
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
This to your routes index.js
var auth = require('./auth');
app.post('/api/auth/login', passport.authenticate('local'),auth.login);
auth.js:
var UserModel = require('../models/user');
var User = new UserModel();
exports.login = function(req, res) {
var user = req.user;
req.login(user, function(err) {
//if error: do something
return res.status(200).json(user)
});
};
Add this to model index.js
var passportLocalMongoose = require('passport-local-mongoose');
userSchema.plugin(passportLocalMongoose, {
usernameField: 'email',
usernameLowerCase: 'true'
});
I'm making a lot of assumptions on structure and packages here. but this should work
EDIT
For custom callbacks:
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);
});
Here you instead of res.redirect you can use something like return res.status(404).json("Not Found)
See docs for more information : http://passportjs.org/guide/authenticate/
Im using NodeJS, ExpressJS, Mongoose, passportJS & connect-ensure-login. Authenticating users works perfectly.
....
var passport = require('passport')
, LocalStrategy = require('passport-local').Strategy
, ensureLoggedIn = require('connect-ensure-login').ensureLoggedIn;
var app = express();
...
app.use(passport.initialize());
app.use(passport.session());
...
passport.use(new LocalStrategy({usernameField: 'email', passwordField: 'password'},
function(email, password, done) {
User.findOne({ 'email': email, 'password': password },
{'_id': 1, 'email':1}, function(err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
return done(null, user);
});
}));
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
app.post('/login', passport.authenticate('local',
{ successReturnToOrRedirect: '/home', failureRedirect: '/login' }));
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
Now, I want to add restrictions to some routes to be accessible only by admin. How can I do that? e.g. /admin/*
var schema = new mongoose.Schema({
name: String,
email: String,
password: String,
isAdmin: { type: Boolean, default: false }
});
mongoose.model('User', schema);
Any hint? Thanks
You could attach a custom middleware to the /admin/* route that would check for admin status before passing the request on the any of the more specific /admin/ routes:
var ensureLoggedIn = require('connect-ensure-login').ensureLoggedIn;
...
var requiresAdmin = function() {
return [
ensureLoggedIn('/login'),
function(req, res, next) {
if (req.user && req.user.isAdmin === true)
next();
else
res.send(401, 'Unauthorized');
}
]
};
app.all('/admin/*', requiresAdmin());
app.get('/admin/', ...);
//Add following function to your app.js above **app.use(app.router)** call;
//This function will be called every time when the server receive request.
app.use(function (req, res, next) {
if (req.isAuthenticated || req.isAuthenticated())
{
var currentUrl = req.originalUrl || req.url;
//Check wheather req.user has access to the above URL
//If req.user don't have access then redirect the user
// to home page or login page
res.redirect('HOME PAGE URL');
}
next();
});
I have not tried it but i think it will work.