I am trying out node_acl with passport-local. When I run my code I cannot secure the route for the admin-user '/admin' and I am redirected to the /login page.
Find below my minimum runnable example:
require('dotenv').config()
const express = require('express')
// const fs = require('fs')
const path = require('path')
const logger = require('morgan')
const bodyParser = require('body-parser')
const cookieParser = require('cookie-parser')
const session = require('express-session')
const passport = require('passport')
const LocalStrategy = require('passport-local').Strategy
const ACL = require('acl')
// load user.json file
// const d = fs.readFileSync(path.join(__dirname, '/../data/user.json'))
// const userObj = JSON.parse(d)
const userObj = [{
id: 1,
username: 'admin',
password: 'admin',
email: 'admin#admin.com',
role: 'admin',
},
{
id: 2,
username: 'user',
password: 'user',
email: 'user#user.com',
role: 'user',
},
]
const app = express()
// view engine setup
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'pug')
app.use(logger(process.env.LOG_ENV))
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
extended: false,
}))
app.use(express.static(path.join(__dirname, '/../public')))
app.use(cookieParser())
app.use(session({
secret: 'super-mega-hyper-secret',
resave: false,
saveUninitialized: false,
}))
/**
* Passport Local
*/
app.use(passport.initialize())
app.use(passport.session())
function authenticate() {
passport.serializeUser((user, done) => {
done(null, user.id)
})
passport.deserializeUser(async(id, done) => {
// const user = await serviceAuth.findById(id)
const user = userObj.find(item => item.id === id)
done(null, user)
})
// Sign in with username and Password
passport.use('local', new LocalStrategy({
usernameField: 'username',
}, async(username, password, done) => {
const user = userObj.find(item => item.username === username)
done(null, user)
}))
}
const isAuthenticated = (req, res, next) => {
if (req.isAuthenticated()) {
res.locals.user = req.session.user
return next()
}
res.redirect('login')
}
authenticate()
/**
* Node ACL
*/
function accessControl() {
const nodeAcl = new ACL(new ACL.memoryBackend())
nodeAcl.allow([{
roles: 'admin',
allows: [{
resources: '/admin',
permissions: '*',
}],
}, {
roles: 'user',
allows: [{
resources: '/dashboard',
permissions: 'get',
}],
}, {
roles: 'guest',
allows: [],
}])
// Inherit roles
// Every user is allowed to do what guests do
// Every admin is allowed to do what users do
nodeAcl.addRoleParents('user', 'guest')
nodeAcl.addRoleParents('admin', 'user')
nodeAcl.addUserRoles(1, 'admin')
nodeAcl.addUserRoles(2, 'user')
nodeAcl.addUserRoles(0, 'guest')
return nodeAcl
}
/*
function checkPermission(resource, action) {
const access = accessControl()
return (req, res, next) => {
const uid = req.session.user.id
access.isAllowed(uid, resource, action, (err, result) => {
if (result) {
next()
} else {
const checkError = new Error('User does not have permission to perform this action on this resource')
next(checkError)
}
})
}
} */
const getCurrentUserId = (req) => {
console.log(req)
req.user && req.user.id.toString() || false
}
const access = accessControl()
// Routes
app.get('/login', (req, res) => {
res.render('login')
})
app.post('/login', (req, res, next) => {
passport.authenticate('local', (err, user) => {
if (err) return next(err)
if (!user) {
return res.status(401).json({
error: 'Email or password is incorrect.',
})
}
return res.render('dashboard')
})(req, res, next)
})
app.get('/dashboard', [isAuthenticated, access.middleware()], (req, res) => {
res.render('dashboard')
})
app.get('/admin', [isAuthenticated, access.middleware()], (req, res) => {
res.render('admin')
})
app.get('/status', (request, response) => {
access.userRoles(getCurrentUserId(request), (error, roles) => {
response.send(`User: ${JSON.stringify(request.user)} Roles: ${JSON.stringify(roles)}`)
})
})
// Start Server
const port = process.env.APP_PORT || 8080
const host = process.env.APP_URL || 'localhost'
app.listen(port, host, () => {
console.log(`Listening on ${host}:${port}`)
})
module.exports = app
Any suggestions why the route, /admin cannot be called only be the admin user?
Thank you in advance for your replies!
I couldn't run your "runnable" code so i changed it a bit to check it out.
So after some tests it seems that it works just fine. Can you check it too?
Using POSTMAN I did a POST on /login?username=user&password=user
After that I did a GET on `/status' and I got
User: {"id":2,"username":"user","password":"user","email":"user#user.com","role":"user"} Roles: []
Then I did a GET on /admin and i got
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>HttpError: Insufficient permissions to access resource
<br> at C:\Users\stamoulis.zamanis\Desktop\aclTest\node_modules\acl\lib\acl.js:705:14
<br> at tryCatcher (C:\Users\stamoulis.zamanis\Desktop\aclTest\node_modules\bluebird\js\release\util.js:16:23)
<br> at Promise.successAdapter [as _fulfillmentHandler0] (C:\Users\stamoulis.zamanis\Desktop\aclTest\node_modules\bluebird\js\release\nodeify.js:23:30)
<br> at Promise._settlePromise (C:\Users\stamoulis.zamanis\Desktop\aclTest\node_modules\bluebird\js\release\promise.js:566:21)
<br> at Promise._settlePromise0 (C:\Users\stamoulis.zamanis\Desktop\aclTest\node_modules\bluebird\js\release\promise.js:614:10)
<br> at Promise._settlePromises (C:\Users\stamoulis.zamanis\Desktop\aclTest\node_modules\bluebird\js\release\promise.js:693:18)
<br> at Async._drainQueue (C:\Users\stamoulis.zamanis\Desktop\aclTest\node_modules\bluebird\js\release\async.js:133:16)
<br> at Async._drainQueues (C:\Users\stamoulis.zamanis\Desktop\aclTest\node_modules\bluebird\js\release\async.js:143:10)
<br> at Immediate.Async.drainQueues (C:\Users\stamoulis.zamanis\Desktop\aclTest\node_modules\bluebird\js\release\async.js:17:14)
<br> at runCallback (timers.js:789:20)
<br> at tryOnImmediate (timers.js:751:5)
<br> at processImmediate [as _immediateCallback] (timers.js:722:5)
</pre>
</body>
</html>
Then I logged in as admin and when I called /admin again i got admin
I had to change app.post('/login'). If i didnt do req.login then passport.serializeUser was never called, the cookie wasn't correct resulting in bad session.
app.post('/login', (req, res, next) => {
passport.authenticate('local', (err, user) => {
if (err) return next(err)
if (!user) {
return res.status(401).json({
error: 'Email or password is incorrect.',
})
}
req.logIn(user, function (err) { // <-- Log user in
next();
});
})(req, res, next)
},function(req,res){
res.send('dashboard')
})
All code:
require('dotenv').config()
const express = require('express')
// const fs = require('fs')
const path = require('path')
const logger = require('morgan')
const bodyParser = require('body-parser')
const cookieParser = require('cookie-parser')
const session = require('express-session')
const passport = require('passport')
const LocalStrategy = require('passport-local').Strategy
const ACL = require('acl')
// load user.json file
// const d = fs.readFileSync(path.join(__dirname, '/../data/user.json'))
// const userObj = JSON.parse(d)
const userObj = [{
id: 1,
username: 'admin',
password: 'admin',
email: 'admin#admin.com',
role: 'admin',
},
{
id: 2,
username: 'user',
password: 'user',
email: 'user#user.com',
role: 'user',
},
]
const app = express()
// view engine setup
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'pug')
app.use(logger(process.env.LOG_ENV))
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({
extended: false,
}))
app.use(express.static(path.join(__dirname, '/../public')))
app.use(cookieParser())
app.use(session({
secret: 'super-mega-hyper-secret',
resave: false,
saveUninitialized: false,
}))
function authenticate() {
passport.serializeUser((user, done) => {
done(null, user.id)
})
passport.deserializeUser((id, done) => {
// const user = await serviceAuth.findById(id)
const user = userObj.find(item => item.id === id)
done(null, user)
})
// Sign in with username and Password
passport.use('local', new LocalStrategy({
usernameField : 'username',
passwordField : 'password'
}, async(username, password, done) => {
const user = userObj.find(item => item.username === username)
done(null, user)
}))
}
const isAuthenticated = (req, res, next) => {
if (req.isAuthenticated()) {
res.locals.user = req.session.user
return next()
}
res.redirect('login')
}
authenticate()
/**
* Passport Local
*/
app.use(passport.initialize())
app.use(passport.session())
/**
* Node ACL
*/
function accessControl() {
const nodeAcl = new ACL(new ACL.memoryBackend())
nodeAcl.allow([{
roles: 'admin',
allows: [{
resources: '/admin',
permissions: '*',
}],
}, {
roles: 'user',
allows: [{
resources: '/dashboard',
permissions: 'get',
}],
}, {
roles: 'guest',
allows: [],
}])
// Inherit roles
// Every user is allowed to do what guests do
// Every admin is allowed to do what users do
nodeAcl.addRoleParents('user', 'guest')
nodeAcl.addRoleParents('admin', 'user')
nodeAcl.addUserRoles(1, 'admin')
nodeAcl.addUserRoles(2, 'user')
nodeAcl.addUserRoles(0, 'guest')
return nodeAcl
}
/*
function checkPermission(resource, action) {
const access = accessControl()
return (req, res, next) => {
const uid = req.session.user.id
access.isAllowed(uid, resource, action, (err, result) => {
if (result) {
next()
} else {
const checkError = new Error('User does not have permission to perform this action on this resource')
next(checkError)
}
})
}
} */
const getCurrentUserId = (req) => {
console.log(req)
req.user && req.user.id.toString() || false
}
const access = accessControl()
// Routes
app.get('/login', (req, res) => {
res.send('login')
})
app.post('/login', (req, res, next) => {
passport.authenticate('local', (err, user) => {
if (err) return next(err)
if (!user) {
return res.status(401).json({
error: 'Email or password is incorrect.',
})
}
req.logIn(user, function (err) { // <-- Log user in
next();
});
})(req, res, next)
},function(req,res){
res.send('dashboard')
})
app.get('/dashboard', [isAuthenticated, access.middleware()], (req, res) => {
res.send('dashboard')
})
app.get('/admin', [isAuthenticated, access.middleware()], (req, res) => {
res.send('admin')
})
app.get('/status', (request, response) => {
access.userRoles(getCurrentUserId(request), (error, roles) => {
response.send(`User: ${JSON.stringify(request.user)} Roles: ${JSON.stringify(roles)}`)
})
})
// Start Server
const port = process.env.APP_PORT || 3335
const host = process.env.APP_URL || 'localhost'
app.listen(port, host, () => {
console.log(`Listening on ${host}:${port}`)
})
module.exports = app
Related
I have installed express-session before initializing passport yet the passport authenticate doesn't works and it simply redirects to the failureRedirect url without any errors or messages.
i have checked that passport initializing works perfectly just authentication is not working
my index.js file
const ejs = require("ejs");
const url = require("url");
const session = require("express-session");
const cookieParser = require('cookie-parser')
const flash = require('connect-flash');
const toastr = require('express-toastr');
const Profile = require('./models/Profile.js');
const passport = require("passport");
const { customAlphabet } = require('nanoid');
const express = require("express"),
path = require('path')
const app = express();
const port = 3000;
const { Notyf } = require('notyf')
const { connect } = require('mongoose');
const LocalStrategy = require('passport-local');
const MemoryStore = require("memorystore")(session);
const methodOverride = require('method-override')
connect(process.env.mongodb);
const initializePassport = require('./utils/passport-config.js')
initializePassport(
passport,
email => Profile.find({ email_id: email }),
id => Profile.find({ user_id: email })
)
var bodyParser = require('body-parser');
app.use(cookieParser());
app.use(
session({
store: new MemoryStore({ checkPeriod: 86400000 }),
secret: "##%#&^$^$%#$^$&%#$%##$%$^%&$%^#$%##$%#E%#%#$FEErfgr3g#%GT%536c53cc6%5%tv%4y4hrgrggrgrgf4n",
resave: false,
saveUninitialized: false,
}),
);
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
app.use(toastr());
app.use('/assets', express.static(path.join(__dirname, 'assets')))
app.use('/scripts', express.static(path.join(__dirname, 'node_modules')))
app.set('views', './views');
app.set('view engine', 'ejs');
app.use(bodyParser.json());
app.use(
bodyParser.urlencoded({
extended: true,
}),
);
const checkAuth = (req, res, next) => {
try {
if (req.isAuthenticated()) return next();
req.session.backURL = req.url;
res.redirect("/login");
} catch (e) {
console.log(e)
}
};
app.get('/', async function(req, res){
res.render('index.ejs', { url: req.url })
});
app.get('/login', async function(req, res){
if (req.session.backURL) {
req.session.backURL = req.session.backURL;
} else if (req.headers.referer) {
const parsed = url.parse(req.headers.referer);
if (parsed.hostname === app.locals.domain) {
req.session.backURL = parsed.path;
}
} else {
req.session.backURL = "/";
}
res.render('login.ejs', { url: req.url })
});
app.get('/signup', async function(req, res){
res.render('signup.ejs', { url: req.url })
});
app.get('/newshop', async function(req, res){
res.render('shopcreate.ejs', { url: req.url })
});
app.get('/profile', async function(req, res){
res.render('profile.ejs', { url: req.url })
});
app.get('/settings', async function(req, res){
res.render('settings.ejs', { url: req.url })
});
app.post('/login', passport.authenticate('local', {
failureRedirect: '/login',
}), async (
err,
req,
res,
next,
) => {
try {
await console.log(req)
if (req.session.backURL) {
const backURL = req.session.backURL;
req.session.backURL = null;
res.redirect(backURL);
} else {
res.redirect('/');
}
} catch(e) {
console.log(e)
}
})
app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`));
my passport-config.js file -
const LocalStrategy = require('passport-local').Strategy
const bcrypt = require('bcrypt')
function initialize(passport, getUserByEmail, getUserById) {
const authenticateUser = async (email, password, done) => {
console.log(email)
const user = getUserByEmail(email)
if (user == null) {
return done(null, false, { message: 'User not found' })
}
try {
if (await bcrypt.compare(password, user.password)) {
return done(null, user)
} else {
return done(null, false, { message: 'Password incorrect' })
}
} catch (e) {
return done(e)
}
}
passport.use(new LocalStrategy({ usernameField: 'email' }, authenticateUser))
passport.serializeUser((user, done) => done(null, user.id))
passport.deserializeUser((id, done) => {
return done(null, getUserById(id))
})
}
module.exports = initialize
I'm trying to authenticate users locally using the passport-jwt strategy or with their google account using passport-google-oauth20 strategy. The local authentication with passport-jwt works fine but when I try to access the protected route after google signup, req.isAuthenticated() keeps returning false.
Here's what my code looks like
passport-jwt config
const passport = require("passport");
const JwtStrategy = require("passport-jwt").Strategy;
const ExtractJwt = require("passport-jwt").ExtractJwt;
const User = require("../models/User");
const opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = process.env.JWT_SECRET;
passport.use(
new JwtStrategy(opts, (jwt_payload, done) => {
User.findOne({ id: jwt_payload.id }, function (err, user) {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
}
});
})
);
passport-google-oauth20 config
const passport = require("passport");
const GoogleStrategy = require("passport-google-oauth20").Strategy;
const User = require("../models/User");
passport.use(
new GoogleStrategy(
{
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: "http://localhost:5000/auth/google/protected",
},
(accessToken, refreshToken, profile, cb) => {
// console.log(profile);
const userInfo = {
googleId: profile.id,
fullname: profile.displayName,
email: profile.emails[0].value,
};
User.findOrCreate(userInfo, (err, user) => {
return cb(err, user);
});
}
)
);
controllers/auth.js
const User = require("../models/User");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const createError = require("../utils/error");
const jsonToken = require("../utils/verifyToken");
const passport = require("passport");
module.exports = {
googleAuth: passport.authenticate("google", {
session: false,
scope: ["email", "profile"],
}),
googleAuthRedirect: passport.authenticate("google", {
session: false,
failureRedirect: "auth/login",
}),
authRedirectCallBack: (req, res) => {
// const token = jsonToken.createToken({
// id: req.user._id,
// isAdmin: req.user.isAdnmin,
// services: req.user.services,
// });
// req.headers["Authorization"] = "Bearer " + token;
// console.log(req.headers["Authorization"]);
console.log(req.isAuthenticated()); //this line returns true
res.redirect("/api/protected");
},
};
routes/auth.js
const express = require("express");
const router = express.Router();
const controllers = require("../controllers/auth");
const {
googleAuth,
googleAuthRedirect,
authRedirectCallBack,
} = controllers;
router.route("/google").get(googleAuth);
router.route("/google/protected").get(googleAuthRedirect, authRedirectCallBack);
module.exports = router;
routes/protected.js
const router = require("express").Router();
const passport = require("passport");
router.get(
"/protected",
(req, res, next) => {
console.log("protected route returns: " + req.isAuthenticated()); //this line returns false
if (req.isAuthenticated()) {
res.send("this is the protected route");
} else {
return res.send("you're not authorized");
}
next();
},
passport.authenticate("jwt", { session: false }),
(req, res) => {
res.send("this is the protected route");
}
);
module.exports = router;
finally, server.js
const express = require("express");
const app = express();
const dotenv = require("dotenv");
dotenv.config();
const mongoose = require("mongoose");
const passport = require("passport");
require("./config/passportJwt");
require("./config/passportGoogle");
mongoose.connect("mongodb://localhost:27017/findersDB", {
useUnifiedTopology: true,
useNewUrlParser: true,
});
mongoose.connection.on("connected", () => console.log("DB connected!"));
app.use(express.json());
app.use(passport.initialize());
const authRoute = require("./routes/auth");
const protectedRoute = require("./routes/protected");
app.use("/auth", authRoute);
app.use("/api", protectedRoute);
app.use((err, req, res, next) => {
const errorMessage = err.message || "Something went wrong";
const errorStatus = err.status || 500;
return res.status(errorStatus).json({
success: false,
message: errorMessage,
status: errorStatus,
stack: err.stack,
});
});
app.listen(5000, () => console.log("Server is running on port 5000"));
I've tried every solution I've seen so far but none seems to work. Any help will be very much appreciated.
this is my local strategy configuration
const passport = require('passport')
const LocalStrategy = require('passport-local').Strategy
const LocalUsers = require('../db/localUser')
const bcrypt = require('bcrypt')
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'password'
},
(username, password, done) => {
LocalUsers.findOne({ email: username }, async (err, user) => {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect email.' });
}
const matchedPassword = await bcrypt.compare(password, user.password)
if (!matchedPassword) {
return done(null, false, { message: 'Incorrect password.' });
}
else{
return done(null, user);
}
});
}
));
passport.serializeUser(function (user, done) {
return done(null, user.id);
});
passport.deserializeUser(async (id, done) => {
await LocalUsers.findById(id, function (err, user) {
done(err, user);
});
});
and this is the server file
require('dotenv').config()
const express = require('express')
const app = express()
const authRoute = require('./routes/auth')
const loginRoute = require('./routes/login')
const homeRoute = require('./routes/home')
const signupRoute = require('./routes/signup')
const passport = require('passport')
const session = require('express-session')
const MongoStore = require('connect-mongo')
require('./db/connection')
require('./config/local-passport-setup')
require('./config/google-passport-setup')
const store = MongoStore.create({ mongoUrl: 'mongodb://localhost:27017/quora', collectionName: 'sessions' })
app.use(express.json())
app.use(express.urlencoded({ extended: false }))
app.use(session({
resave: true,
saveUninitialized: true,
secret:'qwerty',
store: store,
cookie: {
maxAge: 24 * 60 * 60 * 1000
}
}))
app.use(passport.initialize())
app.use(passport.session())
app.set('view engine', 'hbs')
app.use('/', express.static(__dirname + '/public'));
app.get('/', (req, res) => {
if (!req.user) {
res.redirect('/login')
}
else {
res.redirect('/home')
}
})
app.use('/', homeRoute)
app.use('/login', loginRoute)
app.use('/auth', authRoute)
app.use('/', signupRoute)
app.listen(3000, () => {
console.log('server started on http://localhost:3000')
})
here I am calling the passport.authenticate function
onst express = require('express')
const route = express.Router()
const passport = require('passport')
require('../config/local-passport-setup')
route.get('/', (req, res) => {
res.render('login')
})
route.post('/', passport.authenticate('local', { successRedirect: '/home', failureRedirect: '/login' }))
module.exports = route
here the serialize user and deserialize user functions are never being called and thus the req.user is undefined
here the user is not getting authenticated although this oAuth code is working properly
const passport = require('passport')
const GoogleStrategy = require('passport-google-oauth20').Strategy
const GoogleUsers = require('../db/googleUser')
passport.use(new GoogleStrategy(
{
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: 'http://localhost:3000/auth/google/redirect'
},
(accessToken, refreshToken, profile, done) => {
GoogleUsers.findOne({ googleId: profile.id })
.then((currentUser) => {
if (currentUser) {
console.log('current user: ', currentUser)
done(null, currentUser)
}
else {
const newUser = new GoogleUsers({
googleId: profile.id,
name: profile.displayName
})
newUser.save()
.then((newUser) => {
console.log('new user: ', newUser)
done(null, newUser)
})
}
})
}
))
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(async (id, done) => {
await GoogleUsers.findById(id, function (err, user) {
done(err, user);
});
});
Please help me out I have been figuring out the error for a lot time now
I am making a authentication form were users can register and login.
if (process.env.Node_ENV !== 'production') {
require('dotenv').config()
}
const express = require('express');
const app = express();
const bcrypt = require('bcrypt')
const passport = require("passport")
const flash = require("express-flash")
const sessions = require("express-session")
const methodOverride = require("method-override")
const fs = require('fs')
const initializePassport = require("./passport-config")
initializePassport(
passport,
email => users.find(user => user.email === email),
id => users.find(user => user.id === id)
)
const users = []
app.set("view-engine", "ejs")
app.use('/public', express.static('public'))
app.use(express.urlencoded({extended: false}))
app.use(flash())
app.use(sessions({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false
}))
app.use(passport.initialize())
app.use(passport.session())
app.use(methodOverride('_method'))
app.get("/", checkAuthenticated, (req,res)=> {
res.render("index.ejs", { name: req.user.name})
})
app.get("/login", checkNotAuthenticated, (req, res) => {
res.render("login.ejs")
})
app.post("/login", checkNotAuthenticated, passport.authenticate('local', {
successRedirect: "/",
failureRedirect: "/login",
failureFlash: true
}))
app.get("/register", checkNotAuthenticated, (req,res)=> {
res.render("register.ejs")
})
app.post("/register", checkNotAuthenticated, async (req,res) => {
try{
//encrypt the Uses password, so "we" cant see it
const hashedPassword = await bcrypt.hash(req.body.password, 10)
users.push({
id: Date.now().toString(),
name: req.body.name,
email: req.body.email,
password: hashedPassword
})
//if all the above is right, than redirect the user to login page
res.redirect("/login")
}catch{
//if for some reason there is a failure, redirect back to register
res.redirect("/register")
}
//if there is user that is added, it is possible to see ind the console
//console.log(users)
localStorage.setItem('user', JSON.stringify(users));
})
app.delete('/logout', (req,res) => {
req.logOut()
res.redirect('/login')
})
function checkAuthenticated(req, res, next){
if (req.isAuthenticated()) {
return next()
}
res.redirect('/login')
}
function checkNotAuthenticated(req, res, next) {
if (req.isAuthenticated()) {
return res.redirect('/')
}
next()
}
The problem is that I need to implement localStorage, and the user needs to be able to delete the account once they are logged in.
To register the user only needs "Name", "email" and "password"
here is some of my passport-config if necessary:
const LocalStrategy = require('passport-local').Strategy
const bcrypt = require("bcrypt")
function initialize(passport, getUserByEmail, getUserById){
const authenticateUser = async (email, password, done) => {
const user = getUserByEmail(email)
if (user == null) {
return done(null, false, {message: "No user with that Email"})
}
try{
if(await bcrypt.compare(password, user.password)) {
return done(null, user)
} else{
return done(null, false, {message: "Passwrod incorrect"})
}
}catch(error){
return done(error)
}
}
passport.use(new LocalStrategy({usernameField: 'email'}, authenticateUser))
passport.serializeUser((user,done) => done(null, user.id))
passport.deserializeUser((id,done) => {
return done(null, getUserById(id))
})
}
module.exports = initialize;
Everything works fine, so there is no error in the code it's self, I just don't seem to find an answer to what I need to do anywhere, I can only find mongoDB which I don't want to use. The most important for me is to implement the localStorage, then it comes the delete part.
I hope some of you can help me!
Thanks a lot.
I am facing a session problem, Getting req.user undefined after successful passport. authenticate method.
Basically after signUp or login, when I am redirecting, unable to find "user" in request variable.
I have used a hackathon starter which was using mongo, I tried to change things to use Postgres.
Edit -- After following suggestions on Comment
Now signup flow is working fine, but login flow having some problem. Something weird happening, when I am adding a lot of breaking points, It seems to do login intermittently. Update code with comment suggestions
app.js
const express = require('express');
const compression = require('compression');
const session = require('express-session');
const bodyParser = require('body-parser');
const logger = require('morgan');
const chalk = require('chalk');
const errorHandler = require('errorhandler');
const lusca = require('lusca');
const dotenv = require('dotenv');
const flash = require('express-flash');
const path = require('path');
const passport = require('passport');
const expressValidator = require('express-validator');
const expressStatusMonitor = require('express-status-monitor');
const sass = require('node-sass-middleware');
const multer = require('multer');
dotenv.load({ path: '.env.example' });
const SequelizeStore = require('connect-session-sequelize')(session.Store);
const models = require('./models');
const upload = multer({ dest: path.join(__dirname, 'uploads') });
/**
* Load environment variables from .env file, where API keys and passwords are configured.
*/
/**
* Controllers (route handlers).
*/
const homeController = require('./controllers/home');
const userController = require('./controllers/user');
const contactController = require('./controllers/contact');
const dashController = require('./controllers/dash');
const currencyController = require('./controllers/currency');
const accountController = require('./controllers/account');
const testController = require('./controllers/test');
const txController = require('./controllers/transaction');
/**
* API keys and Passport configuration.
*/
const passportConfig = require('./config/passport');
/**
* Create Express server.
*/
const app = express();
/**
* Express configuration.
*/
app.set('host', '127.0.0.1');
app.set('port', 8080);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.use(expressStatusMonitor());
app.use(compression());
app.use(sass({
src: path.join(__dirname, 'public'),
dest: path.join(__dirname, 'public')
}));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(expressValidator());
app.use(session({
resave: true,
saveUninitialized: true,
secret: process.env.SESSION_SECRET,
cookie: { maxAge: 1209600000 }, // two weeks in milliseconds
store: new SequelizeStore({
db: models.sequelize
})
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
app.use((req, res, next) => {
// if (req.path === '/api/upload') {
// next();
// } else {
// lusca.csrf()(req, res, next);
// }
next();
});
app.use(lusca.xframe('SAMEORIGIN'));
app.use(lusca.xssProtection(true));
app.disable('x-powered-by');
app.use((req, res, next) => {
res.locals.user = req.user;
next();
});
app.use((req, res, next) => {
// After successful login, redirect back to the intended page
if (!req.user
&& req.path !== '/login'
&& req.path !== '/signup'
&& !req.path.match(/^\/auth/)
&& !req.path.match(/\./)) {
req.session.returnTo = req.originalUrl;
} else if (req.user
&& (req.path === '/account' || req.path.match(/^\/api/))) {
req.session.returnTo = req.originalUrl;
}
next();
});
app.use('/', express.static(path.join(__dirname, 'public'), { maxAge: 31557600000 }));
app.use('/js/lib', express.static(path.join(__dirname, 'node_modules/chart.js/dist'), { maxAge: 31557600000 }));
app.use('/js/lib', express.static(path.join(__dirname, 'node_modules/popper.js/dist/umd'), { maxAge: 31557600000 }));
app.use('/js/lib', express.static(path.join(__dirname, 'node_modules/bootstrap/dist/js'), { maxAge: 31557600000 }));
app.use('/js/lib', express.static(path.join(__dirname, 'node_modules/jquery/dist'), { maxAge: 31557600000 }));
app.use('/webfonts', express.static(path.join(__dirname, 'node_modules/#fortawesome/fontawesome-free/webfonts'), { maxAge: 31557600000 }));
/**
* Primary app routes.
*/
app.get('/', homeController.index);
app.get('/login', userController.getLogin);
app.post('/login', userController.postLogin);
app.get('/logout', userController.logout);
app.get('/forgot', userController.getForgot);
app.post('/forgot', userController.postForgot);
app.get('/reset/:token', userController.getReset);
app.post('/reset/:token', userController.postReset);
app.get('/signup', userController.getSignup);
app.post('/signup', userController.postSignup);
// app.get('/account', passportConfig.isAuthenticated, userController.getAccount);
// app.post('/account/profile', passportConfig.isAuthenticated, userController.postUpdateProfile);
app.post('/account/password', passportConfig.isAuthenticated, userController.postUpdatePassword);
// app.post('/account/delete', passportConfig.isAuthenticated, userController.postDeleteAccount);
// app.get('/account/unlink/:provider', passportConfig.isAuthenticated, userController.getOauthUnlink);
app.get('/dashboard', passportConfig.isAuthenticated, dashController.index);
app.get('/test', passportConfig.isAuthenticated, testController.test);
// app.get('/contact', contactController.contact);
// app.post('/addContact', contactController.addContact);
// app.post('/editContact', contactController.editContact);
// app.get('/listContacts', contactController.listContacts);
// app.get('/listCurrency', currencyController.listCurrency);
// app.post('/addAccount', accountController.addAccount);
// app.post('/editAccount', accountController.editAccount);
// app.get('/listAccounts', accountController.listAccounts);
// app.get('/getTransactions', txController.getTransactions);
/**
* Error Handler.
*/
if (process.env.NODE_ENV === 'development') {
// only use in development
app.use(errorHandler());
} else {
app.use((err, req, res, next) => {
console.error(err);
res.status(500).send('Server Error');
});
}
/**
* Start Express server.
*/
models.sequelize.sync({}).then(() => {
app.listen(app.get('port'), () => {
console.log('%s App is running at http://localhost:%d in %s mode', chalk.green('✓'), app.get('port'), app.get('env'));
console.log('Press CTRL-C to stop\n');
});
});
module.exports = app;
user.js
const { promisify } = require('util');
const crypto = require('crypto');
const nodemailer = require('nodemailer');
const passport = require('passport');
const models = require('../models');
const { User } = models;
const randomBytesAsync = promisify(crypto.randomBytes);
/**
* GET /login
* Login page.
*/
exports.getLogin = (req, res) => {
if (req.user) {
return res.redirect('/');
}
res.render('account/login', {
title: 'Login'
});
};
/**
* POST /login
* Sign in using email and password.
*/
exports.postLogin = (req, res, next) => {
req.assert('email', 'Email is not valid').isEmail();
req.assert('password', 'Password cannot be blank').notEmpty();
req.sanitize('email').normalizeEmail({ gmail_remove_dots: false });
const errors = req.validationErrors();
if (errors) {
req.flash('errors', errors);
return res.redirect('/login');
}
passport.authenticate('local', (err, user, info) => {
if (err) { return next(err); }
if (!user) {
req.flash('errors', info);
return res.redirect('/login');
}
req.logIn(user, (err) => {
if (err) { return next(err); }
res.locals.user = user; //Updated code after comment suggestions
req.flash('success', { msg: 'Success! You are logged in.' });
res.redirect('/dashboard');
});
})(req, res, next);
};
/**
* GET /logout
* Log out.
*/
exports.logout = (req, res) => {
req.logout();
req.session.destroy((err) => {
if (err) console.log('Error : Failed to destroy the session during logout.', err);
req.user = null;
res.redirect('/');
});
};
/**
* GET /signup
* Signup page.
*/
exports.getSignup = (req, res) => {
if (req.user) {
return res.redirect('/');
}
res.render('account/signup', {
title: 'Create Account'
});
};
/**
* POST /signup
* Create a new local account.
*/
exports.postSignup = (req, res, next) => {
req.assert('email', 'Email is not valid').isEmail();
req.assert('password', 'Password must be at least 4 characters long').len(4);
req.assert('confirmPassword', 'Passwords do not match').equals(req.body.password);
req.sanitize('email').normalizeEmail({ gmail_remove_dots: false });
const errors = req.validationErrors();
if (errors) {
req.flash('errors', errors);
return res.redirect('/signup');
}
User.findAll({ limit: 1, where: { email: req.body.email }, plain: true })
.then((existingUser) => {
if (existingUser) {
req.flash('errors', { msg: 'Account with that email address already exists.' });
return res.redirect('/signup');
}
User.create({
email: req.body.email,
password: req.body.password
}).then((user) => {
req.logIn(user, (err) => {
if (err) { return next(err); }
res.locals.user = user; //updated code after comment suggestions
return res.redirect('/');
});
}).catch(error => next(error));
});
};
passport.js
const passport = require('passport');
const request = require('request');
const { Strategy: LocalStrategy } = require('passport-local');
const _ = require('lodash');
const models = require('../models');
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
models.User.findAll({ where: { id }, limit: 1, plain: true })
.then(user => done(null, user))
.catch(err => done(err));
});
/**
* Sign in using Email and Password.
*/
passport.use(new LocalStrategy({ usernameField: 'email' }, (email, password, done) => {
models.User.findAll({ where: { email }, limit: 1, plain: true }).then((user) => {
if (!user) {
return done(null, false, { msg: `Email ${email} not found.` });
}
user.comparePassword(password, (err, isMatch) => {
if (err) { return done(err); }
if (isMatch) {
return done(null, user);
}
return done(null, false, { msg: 'Invalid email or password.' });
});
}).catch(error => done(error));
}));
/**
* OAuth Strategy Overview
*
* - User is already logged in.
* - Check if there is an existing account with a provider id.
* - If there is, return an error message. (Account merging not supported)
* - Else link new OAuth account with currently logged-in user.
* - User is not logged in.
* - Check if it's a returning user.
* - If returning user, sign in and we are done.
* - Else check if there is an existing account with user's email.
* - If there is, return an error message.
* - Else create a new account.
*/
/**
* Login Required middleware.
*/
exports.isAuthenticated = (req, res, next) => {
if (req.isAuthenticated()) {
return next();
}
res.redirect('/login');
};
/**
* Authorization Required middleware.
*/
exports.isAuthorized = (req, res, next) => {
const provider = req.path.split('/').slice(-1)[0];
const token = req.user.tokens.find(token => token.kind === provider);
if (token) {
next();
} else {
res.redirect(`/auth/${provider}`);
}
};
In your app.js, you need to call const passportConfig = require('./config/passport'); after you initialize passport, like so:
app.use(passport.initialize());
app.use(passport.session());
// place it here
const passportConfig = require('./config/passport')(passport);
Further, you need to adjust your password config file (./config/passport), so that on module loading it can get the passport instance that you instantiated in app.js, and subsequently uses it instead of loading a new passport instance via require('passport') - this in my opinion messes things up; didn't test it though. :)
Can you share the user object sample which refer to this code in your passport.js LocalStrategy
user.comparePassword(password, (err, isMatch) => {....});
looks like user.id is not available in serializeuser below which may be the cause it cant set user.id in the session and cant deserialize User further not setting the user object in req
passport.serializeUser((user, done) => {
done(null, user.id);
});