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.
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 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'm trying Nextjs for the first time and I added express to use mongodb. I got so far as getting login in with my google credentials but I'm struggling getting the session info to my user's profile page.
Server.js:
const express = require('express');
const next = require('next');
const bodyParser = require("body-parser");
const mongoose = require("mongoose");
mongoose.Promise = global.Promise;
const passport = require("passport");
const LocalStrategy = require("passport-local");
const exsession = require("express-session");
const User = require('../models/User');
const port = process.env.PORT || 8080;
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
const middleware = require("../middleware/middleware")
const uri = "XXXX"
app.prepare()
.then(() => {
const server = express();
const showRoutes = require('./routes/index');
const authRouter = require('./routes/auth');
mongoose.connect(uri)
.then(function () {
console.log('Connected to MONGOD !!');
}).catch(function (err) {
console.log('Failed to establish connection with MONGOD !!');
console.log(err.message);
});
server.use(express.static(__dirname + "/public"));
server.use(bodyParser.urlencoded({extended: true}));
server.use(bodyParser.json());
/////////////////////////////////////
//passport configuration
/////////////////////////////////////
server.use(exsession({
secret: "projectx",
resave: false,
saveUninitialized: false,
}));
server.use(passport.initialize());
server.use(passport.session());
passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
server.use(function(req, res, next){
res.locals.currentUser = req.user;
next();
})
server.use('/api', showRoutes);
server.use('/auth', authRouter);
server.get('/login', (req, res) => {
return app.render(req, res, '/login', req.query )
})
server.get('/profile', middleware.isLoggedIn, (req, res, next) => {
console.log(req.query)
return app.render(req, res, '/profile', { user: req.user } )
})
server.get('/post/:id', (req, res) => {
return app.render(req, res, '/post', { id: req.params.id })
})
server.get("*", (req, res) => {
return handle(req,res);
})
server.listen(port, err => {
if (err) throw err;
console.log(`> Ready on http://localhost:${port}`)
})
})
.catch(ex => {
console.log(ex.stack);
process.exit(1);
})
Profile.js:
import React, { Component } from 'react'
export default class extends Component {
static getInitialProps ({ user: { user } }) {
console.log(user)
return { user: user }
}
render () {
return <div>
<h1>Welcome{this.props.user}</h1>
</div>
}
}
I'm trying to display my current user's name in my profile page however i get undefined in my console.
So recently I came across a problem, which is happening when i try to authenticate using google oauth2 from mobile, but when i try to authenticate via computer, it works fine. I am using it with passportjs in node/express project.
project link: https://rhubarb-tart-18821.herokuapp.com/
You guys can see code here:
https://ide.c9.io/saijax/www
just whole authentication is so big that i can not put everything here...
EDIT:
here are some main files
passport.js
const GoogleStrategy = require("passport-google-oauth2")
.Strategy;
const mongoose = require("mongoose");
const keys = require("./keys");
module.exports = (passport) => {
passport.use(
new GoogleStrategy({
clientID: keys.googleClientID,
clientSecret: keys.googleClientSecret,
callbackURL: "/auth/google/callback",
proxy: true
}, (accessToken, refreshToken, profile, done) => {
const image = profile.photos[0].value.substring(0, profile.photos[0].value.indexOf("?"));
const newUser = {
googleID: profile.id,
email: profile.emails[0].value,
firstName: profile.name.givenName,
lastName: profile.name.familyName,
image: image
}
// CHECK FOR USER
User.findOne({
googleID: profile.id
}).then(user => {
if(user){
done(null, user);
} else {
// CREATE USER
new User(newUser)
.save()
.then(user => done(null,user));
}
})
})
);
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id)
.then(user => done(null, user));
});
}
auth.js
const express = require("express");
const passport = require("passport");
const router = express.Router();
router.get("/google", passport.authenticate("google", {
scope: [
"profile",
"email"
]
}));
router.get("/google/callback", passport.authenticate("google", {
failureRedirect: "/"
}), (req, res) => {
req.flash("success_msg", "Successfully Logged In");
res.redirect("/dashboard");
});
router.get("/verify", (req, res) => {
if(req.user){
console.log(req.user);
} else {
console.log("Not auth");
}
});
router.get('/logout', (req, res) => {
req.logout();
req.flash("success_msg", "Successfully Logged Out");
res.redirect('/');
});
module.exports = router;
app.js
// SETUP
const express = require("express");
const mongoose = require("mongoose");
const passport = require("passport");
const cookieParser = require("cookie-parser");
const session = require("express-session");
const exphbs = require("express-handlebars");
const bodyParser = require("body-parser");
const methodOverride = require("method-override");
const flash = require("connect-flash");
// LOAD GOOGLE AND MONGO KEYS
const keys = require("./config/keys");
// LOAD MODELS
require("./models/story");
require("./models/user");
// PASSPORT CONFIG
require("./config/passport")(passport);
// LOAD ROUTES
const auth = require("./routes/auth");
const index = require("./routes/index");
const stories = require("./routes/stories");
// HANLEBARS HELPERS
const {
truncate,
stripTags,
formatDate,
select,
editIcon
} = require("./helpers/hbs");
// MONGOOSE CONNECT
mongoose.connect(keys.mongoURI)
.then(() => {
console.log("MongoDB Connected...");
}).catch(err => console.log(err));
// USE APP
const app = express();
// CSS CONFIG
app.use(express.static(__dirname + "/public"));
// VIEW ENGINE
app.engine("handlebars", exphbs({
helpers: {
truncate: truncate,
stripTags: stripTags,
formatDate: formatDate,
select: select,
editIcon: editIcon
},
defaultLayout: "main"
}));
app.set("view engine", "handlebars");
// BODY PARSER
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
// METHOD OVERRIDE
app.use(methodOverride("_method"));
// FLASH
app.use(flash());
// COOKIE PARSER
app.use(cookieParser());
// SESSION MIDDLEWARE
app.use(session({
secret: "KAPPA",
resave: false,
saveUninitialized: false
}));
// PASSPORT MIDDLEWARE
app.use(passport.initialize());
app.use(passport.session());
// SET GLOBAL VARS
app.use((req, res, next) => {
res.locals.user = req.user || null;
res.locals.success_msg = req.flash("success_msg");
res.locals.error_msg = req.flash("error_msg");
next();
});
// ROUTES
app.use("/", index);
app.use("/auth", auth);
app.use("/stories", stories);
UPDATE: well, i dont know if it was silly mistake or not, but it seems like mobile can not read
callbackURL: "/auth/google/callback"
instead i changed following line with
callbackURL: keys.callback + "/auth/google/callback"
where keys.callback is url for my app(from c9/heroku) ... now it works perfectly!
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