This is my first post on stackoverflow so I apologize in advance if the form of my post is weird. I'm looking to use sessions with express-session to persist a user's connection in a react application. With PostMan, the cookie is saved, the back-end recognizes the user while from my browser which performs a post request with axios I send a (res.send (req.session)))
receives the session, but after another call is not recognized by the server.
My server code:
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const sha1 = require('sha1');
const cors = require('cors');
const path = require('path');
const cookieParser = require('cookie-parser');
const session = require('express-session');
const React = require('react');
const app = express();const MONGOURI = "mongodb+srv://loulou00:loulou00#cluster0.2t92n.mongodb.net/User?retryWrites=true&w=majority";
//connect to mongoose db
mongoose.connect(MONGOURI, {useNewUrlParser: true})
.then(() => console.log('DB CONNECTED'))
.catch(error => console.log(error));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(express.static(path.join(__dirname, '../build')));
app.use(cors({credentials: true, origin: 'http://localhost:1200', exposedHeaders: ["set-cookie"],}));
let sess = {
secret: 'keyboard cat',
cookie: {
maxAge: 60000
},
resave: true,
saveUninitialized: true
}
if (app.get('env') === 'production') {
app.set('trust proxy', 1) // trust first proxy
sess.cookie.secure = true // serve secure cookies
}
app.use(session(sess))
const { Customer } = require('./models/customer');
const { response } = require('express');
//API ROUTE
app.get('/zeb', function (req, res) {
res.send(req.session)
console.log(req.session)
//res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
})
app.post('/api/token_add', (req, res) => {
if (req.body.secret == 'super secret')
{
Customer.findOne({ name: req.body.name }, (err, response) => {
if (!response)
{
const token = sha1(req.body.name)
const customer = new Customer({
name: req.body.name,
pack: req.body.pack,
token: token,
}).save((e, resp) => {
if(e) res.status(400).send(e);
res.status(200).send(resp);
console.log(resp);
})
}
else
{
res.status(400).send('This name is already use')
}
})
}
})
app.post('/api/token_connect', (req, res) => {
Customer.findOne({ token: req.body.token }, (err, response) => {
if(err) res.status(400).send(err)
if (response) {
req.session.token = req.body.token;
res.status(200).send(req.session);
console.log(req.session)
}
else{
res.send('Invalid token')
}
})
});
app.get('/api/token_getinfo', (req, res) => {
console.log(req.session)
Customer.findOne({ token: req.query.token }, (err, response) => {
if(err) res.status(400).send(err)
if (response) {
res.status(200).send(response);
}
else{
res.send('Invalid token')
}
})
})
app.get('/api/token_connectsess', (req, res) => {
console.log(req.session)
Customer.findOne({ token: req.session.token }, (err, response) => {
if(err) res.status(400).send(err)
if (response) {
res.status(200).send(req.session);
}
else{
res.send('Invalid token')
}
})
});
const port = process.env.PORT || 1200;
app.listen(port, () => {
console.log('Server runnin on ' + port)
})
My axios request:
const connect = () => {
axios.post('http://192.168.1.24:1200/api/token_connect', {token: token}
,{
"headers": {
"content-type": "application/json",
}
}).then((res) => {
if (res.data.token)
{
console.log(res.data.token);
setUser(res.data.token) ;
}
})
}
The session :
Session {
cookie: {
path: '/',
_expires: 2020-10-26T22:14:31.294Z,
originalMaxAge: 60000,
httpOnly: true
},
token: 'f7ed376ba27377ae2680fafe1a67037df80b7e36'
}
you need to pass this {withCredentials: true} as an option in your request
For Example:
axios.post(API_SERVER + '/login', { email, password }, { withCredentials: true })
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 having issues getting access to req.user after I login. The passport authentication works and I initially get the req.user information to send to the client side after logging in but it becomes undefined immediately after.Here is a picture of the console showing this. How can I stop req.user form being undefined after logging in? It looks like it is in the session for a brief moment after the initial login. I've spent hours for fixes but nothing seems to work.
Below is how I handle the login.
router.post(
'/login',
function (req, res, next) {
next()
},
passport.authenticate('local', { failureFlash: true, failureRedirect: '/login' }),
async (req, res) => {
const fullUser = await User.findOne({ username: req.user.username })
console.log(req.user)
console.log(req.session)
res.send(fullUser);
}
)
Below is the server setup.
const express = require('express');
const mongoose = require('mongoose');
const session = require('express-session');
const passport = require('passport');
const LocalStrategy = require('passport-local');
const User = require('./models/user');
const usersRoutes = require('./routes/users-routes');
const chirpsRoutes = require('./routes/chirps-routes');
const singleRoutes = require('./routes/single-routes');
const HttpError = require('./models/http-error');
const MongoDBStore = require('connect-mongo')(session);
const cors = require('cors');
const app = express();
const connectUrl = 'mongodb info removed';
mongoose.connect(connectUrl, {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true,
useFindAndModify: false
});
const db = mongoose.connection;
db.on("error", console.error.bind(console, "connection error:"));
db.once("open", () => {
console.log("Database connected");
});
// const connectConfig = {
// useNewUrlParser: true,
// useCreateIndex: true,
// useUnifiedTopology: true,
// useFindAndModify: false
// }
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true })); //used to parse req.body
const secret = process.env.SECRET || 'thishouldbeabettersecret!';
const store = new MongoDBStore({
url: connectUrl,
secret,
touchAfter: 24 * 60 * 60
});
store.on("error", function (e) {
console.log("SESSION STORE ERROR", e)
})
const sessionConfig = {
name: 'session',
secret,
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
expires: Date.now() + 1000 * 60 * 60 * 24 * 7,
maxAge: 1000 * 60 * 60 * 24 * 7
}
}
app.use(session(sessionConfig))
app.use(passport.initialize());
app.use(passport.session());
passport.use(new LocalStrategy(User.authenticate()));
//Add user to session
passport.serializeUser(User.serializeUser());
//Remove user from session
passport.deserializeUser(User.deserializeUser());
// middleware
app.use((req, res, next) => {
// res.locals.currentUser = req.user; //passport user
res.setHeader('Access-Control-Allow-Origin', '*'); //set header on resposne
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-Width, Content-Type, Accept, Authorization'); //incoming requests handle
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PATCH, DELETE');
console.log('******REQ.USER******' + " " + req.user)
console.log('******SESSION BELOW******')
console.log(req.session)
next();
})
app.use('/auth', usersRoutes)
app.use('/chirps', chirpsRoutes)
app.use('/:uid', singleRoutes)
app.use((req, res, next) => { //error handling for invalid routes
const error = new HttpError('Could not find this route.', 404);
return next(error);
})
app.use((error, req, res, next) => {
if (res.headerSent) {
return next(error);
}
res.status(error.code || 500);
res.send({ message: error.message || 'An unknown error occurred!' });
})
const port = process.env.PORT || 5000;
app.listen(port, () => {
console.log(`Serving on port ${port}`)
})
Below is the User schema.
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const passportLocalMongoose = require('passport-local-mongoose');
const UserSchema = new Schema({
email: {
type: String,
required: true,
unique: true
},
about: String
});
//add username & password to UserSchema
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model('User', UserSchema);
And lastly, below is my login post request that I handle with React.
function loginUser(username, password) {
try {
const data = {
username,
password
}
axios.post('http://localhost:5000/auth/login', data)
.then(response => {
console.log(response.data)
if (response.status === 200) {
auth.login(response.data._id,response.data.username)
history.push('/chirps');
}
})
} catch (error) {
console.log(error)
}
}
I'm building a MERN login system using Passport.js. I've set up all the Passport.js basics to properly run on the server-side along with the authentication middleware to protect the routes.
Since it's a MERN project I'm using React as my client-side application I need to make API calls to the backend. But I've noticed that I'm unable to get access to req.user from the backend while trying to fetch the /auth/user endpoint of my express server.
While I'm not unable the get access to req.user from neither my React application nor an API calls program such as Insomnia, I'm able to access it by manually going to the http://localhost:5000/auth/user on the browser.
SERVER:
index.js
const express = require('express');
const mongoose = require('mongoose');
const dotenv = require('dotenv');
const morgan = require('morgan');
const cors = require('cors');
const passport = require('passport');
const session = require('express-session');
const MongoStore = require('connect-mongo')(session);
const connectDB = require('./config/db');
dotenv.config({ path: './config/config.env' });
require('./config/passport')(passport);
const app = express();
app.use(
session({
secret: 'keyboard',
resave: false,
saveUninitialized: false,
// cookie: {
// secure: true,
// },
store: new MongoStore({
mongooseConnection: mongoose.connection,
collection: 'sessions',
}),
})
);
app.use(passport.initialize());
app.use(passport.session());
app.use(cors());
app.use(express.json());
app.use(
express.urlencoded({
extended: false,
})
);
app.use(morgan('dev'));
connectDB();
app.use('/', require('./routes/index'));
app.use('/auth', require('./routes/auth'));
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(
`Server is running in ${process.env.NODE_ENV} mode on port ${PORT}`
);
});
config/passport.js
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const mongoose = require('mongoose');
const User = require('../models/User');
module.exports = (passport) => {
passport.use(
new GoogleStrategy(
{
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: '/auth/google/callback',
},
async (accessToken, refreshToken, profile, done) => {
const newUser = {
googleId: profile.id,
displayName: profile.displayName,
firstName: profile.name.givenName,
lastName: profile.name.familyName,
image: profile.photos[0].value,
};
try {
let user = await User.findOne({
googleId: profile.id,
});
if (user) {
done(null, user);
} else {
user = await User.create(newUser);
done(null, user);
}
} catch (err) {
console.error(err);
}
}
)
);
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
done(err, user);
});
});
};
routes/auth.js
const express = require('express');
const passport = require('passport');
const { ensureAuth } = require('../middleware/auth');
const router = express.Router();
router.get('/google', passport.authenticate('google', { scope: ['profile'] }));
router.get(
'/google/callback',
passport.authenticate('google', {
failureRedirect: 'http://localhost:3000/login',
}),
(req, res) => {
console.log('/google/callback router:', req.user);
res.redirect('http://localhost:3000/dashboard');
}
);
router.get('/user', ensureAuth, (req, res, next) => {
if (req.user) {
res.status(200).send(req.user);
} else {
res.status(200).send({});
}
});
router.get('/logout', (req, res) => {
req.logout();
res.redirect('http://localhost:3000');
});
module.exports = router;
middleware/auth.js
module.exports = {
ensureAuth: function (req, res, next) {
if (req.isAuthenticated()) {
return next();
} else {
res.redirect('http://localhost:3000/login');
}
},
ensureGuest: function (req, res, next) {
if (req.isAuthenticated()) {
res.redirect('http://localhost:3000/dashboard');
} else {
return next();
}
},
};
CLIENT:
/client/src/components/page/Login.js
import React from 'react';
const Login = () => {
return (
<>
<a
style={{
border: '1px solid #0000ff',
borderRadius: '4px',
margin: '40px',
display: 'table',
padding: '5px',
fontWeight: '900',
color: '#0000ff',
textDecoration: 'none',
}}
href='http://localhost:5000/auth/google'
>
Login with Google
</a>
</>
);
};
export default Login;
/client/src/components/page/Dashboard.js
import React, { useEffect, useState } from 'react';
const Dashboard = () => {
const [userInfo, setUserInfo] = useState({});
useEffect(() => {
const fetchUserInfo = async () => {
const response = await fetch('http://localhost:5000/auth/user', {
method: 'GET',
mode: 'cors',
cache: 'no-cache',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
},
});
const data = await response.json();
setUserInfo(data);
};
fetchUserInfo();
}, []);
const fetchLogout = async () => {
const response = await fetch('http://localhost:5000/auth/logout', {
method: 'GET',
mode: 'cors',
cache: 'no-cache',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
},
});
const data = await response.json();
setUserInfo(data);
};
return (
<div>
<h1>Dashboard</h1>
<button
onClick={() => {
fetchLogout();
}}
>
Logout
</button>
</div>
);
};
export default Dashboard;
Any thoughts on how to get access to the req.user from the server to then proceed from there and manipulate the user data on the client-side?
I am developing an API using NodeJS with the functionality of user registration and login. When a user register, I get
"Error: WHERE parameter "email" has invalid "undefined" value" this
error."
I have checked similar question and answer here and tried every one of them, but none has worked for me.
app.js file
```
//use path module
const path = require('path');
//use express module
const express = require('express');
//use ejs view engine
const ejs = require('ejs');
//use bodyParser middleware
const bodyParser = require('body-parser');
//use mysql database
const mysql = require('mysql');
const app = express();
//Setting port number
const port = process.env.PORT || 619;
const mysqlConnection = mysql.createConnection({
host: 'localhost',
user:'root',
password: '',
database: 'home_automation_db'
});
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(function(req, res, next) {
res.locals.stuff = {
url : req.originalUrl
}
next();
});
//connecting to database
mysqlConnection.connect((err) =>{
if(!err)
console.log('DB connection successful');
else
console.log('connection failed \n Error: '+JSON.stringify(err, undefined, 2));
});
var Users = require('./controllers/lightController');
app.use('/users', Users);
//server listening
app.listen(port, () => {
console.log('Server is running at port '+port);
});
lightcontroller.js
```var bodyParser= require('body-parser');
var urlencodedParser = bodyParser.urlencoded({ extended: false });
var generator = require('generate-password');
var nodemailer = require('nodemailer');
const bcrypt = require('bcrypt');
const cors= require('cors')
const jwt = require('jsonwebtoken');
const express =require('express')
const users = express.Router();
const User = require('../models/User');
users.use(cors());
process.env.SECRET_KEY = 'secret';
```
```
// Login controller
users.post('/register', function (req, res) {
const userData = {
user_name: req.body.username,
email: req.body.email,
password: req.body.password,
location: req.body.location,
house_number: req.body.house_number
}
User.findOne({
where: {
email: req.body.email
}
}).then(user => {
if (!user) {
bcrypt.hash(req.body.password, 10, (err, hash) => {
userData.password = hash
User.create(userData).then(user => {
res.json({ status: user.email + "registered" })
}).catch(err => {
res.send('error: ' + err)
})
})
} else {
res.json({ error: "user already exist." })
}
}).catch(err => {
res.send('error: '+err)
})
});
```
User model
const Sequelize = require('sequelize')
const db = require("../database/db")
module.exports = db.sequelize.define(
'user_tb',
{
user_id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true
},
user_name: {
type: Sequelize.STRING
},
email: {
type: Sequelize.STRING
},
password: {
type: Sequelize.STRING
},
location: {
type: Sequelize.STRING
},
house_number: {
type: Sequelize.STRING
}
},
{
timestamps: false,
freezeTableName: true
}
)
As #messerbill said the most probable cause is the sending of data as undefined
Try adding config after post data to your post request on the client side so it sets proper content-type:
axios.post('http://localhost:9000/api/login', {
email: this.state.email,
password: this.state.password,
}, {
'content-type': 'x-www-form-urlencoded'
}).then(res => {
console.log(res);
if(res.status) {
console.log('User is logged in');
}
}).catch(err => console.log(err))
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