This is a project with stack MEAN.
I use passport for authentication, but it doesn't work well.
After logging in, reloading the page, access is immediately requested, not recognizing that the user is authenticated.
This is my code (service, model js, component angular, app.js)
// app.component.ts
constructor(private _modalService: NgbModal, private _user:UserService, private _router:Router) {
this._user.userLogged().subscribe(
data => console.log(data),
error => this._router.navigate(['/login'])
)
}
// user.service.ts
login(body:any) {
return this._http.post('http://127.0.0.1:3000/users/login', body, {
observe: 'body',
withCredentials: true,
headers: new HttpHeaders().append('Content-type', 'application/json')
});
}
userLogged() {
return this._http.get('http://127.0.0.1:3000/users/user-logged', {
observe: 'body',
withCredentials: true,
headers: new HttpHeaders().append('Content-type', 'application/json')
});
}
// users.js
router.post('/login', function (req, res, next) {
passport.authenticate('local',function (err, user, info) {
if (err) return res.status(501).json(err);
if (!user) return res.status(501).json(info);
req.logIn(user, function (err) {
if (err) return res.status(501).json(err);
return res.status(200).json({message: "login ok"});
});
})(req, res, next);
});
router.get('/user-logged', isValidUser, function (req,res,next) {
return res.status(200).json(req.user);
});
function isValidUser(req,res,next) {
if (req.isAuthenticated()) return next();
return res.status(401).json({message: 'Non autorizzato'});
}
// app.js
var passport = require('passport');
var session = require('express-session');
const MongoStore = require('connect-mongo');
app.use(session({
name: 'myname.sid',
resave: false,
saveUninitialized: false,
secret: 'secret',
cookie: {
maxAge: 36000000,
httpOnly: false,
secure: false,
},
store: MongoStore.create({mongoUrl: 'mongodb://localhost/iHospital'})
}));
require('./passport-config');
app.use(passport.initialize());
app.use(passport.session());
Related
I have followed the steps described in the documentation of passportJS to configure local authentication. I pretty much copy-pasted their code to test if it works.
However, I now have 2 (related?) problems:
deserializeUser is never being called
req.user is undefined
I tried pretty much everything (changing the middleware orders, putting withCredentials: true on the client side, ...) that is mentioned in related posts, but nothing seems to work. Does someone maybe see what I'm doing wrong here?
index.js
app.use(express.json()); // to send data to the database
app.use(express.urlencoded({ extended: true }));
app.use(
cors({
origin: "http://localhost:3000", // location of the react app
credentials: true,
})
);
app.use(
session({
secret: "secretcode",
resave: false,
saveUninitialized: false,
})
);
app.use(passport.session());
app.use(cookieParser("secretcode"));
app.use(passport.initialize());
config(passport);
app.post("/login", passport.authenticate('local', { failureRedirect: '/login', failureMessage: true }),
function(req, res) {
res.redirect('/~' + req.user.username);
});
app.get("/user", (req, res) => {
res.send(req.user);
});
config.js
export const config = (passport) => {
passport.use(new LocalStrategy(function verify(username, password, cb) {
db.query('SELECT * FROM users WHERE username = ?', [ username ], function(err, user) {
if (err) { return cb(err); }
if (!user) { return cb(null, false, { message: 'Incorrect username or password.' }); }
bcrypt.compare(password, user[0].password, (err, result) => {
if (err) { return cb(err); }
if (result === true) {
return cb(null, user[0]);
}
return cb(null, false, { message: 'Incorrect username or password.' });
});
});
}));
passport.serializeUser(function(user, cb) {
process.nextTick(function() {
return cb(null, user.id);
});
});
passport.deserializeUser(function(id, cb) {
db.query('SELECT * FROM users WHERE id = ?', [ id ], function(err, user) {
if (err) { return cb(err); }
return cb(null, user);
});
});
};
I believe that your problem may be because you have not included app.use(passport.authenticate('session')). Try that.
You can also debug further by passing a path into the ensureLogIn middleware as I've had problems with that in the past. It helps debug if you know what is being redirected.
Please find a working barebones attached here for a working passport-local and express example without all of the bloat. I think comparing this with your application would be the best bet.
Let me know if this helps.
const express = require('express')
const passport = require('passport')
const LocalStrategy = require('passport-local')
const session = require('express-session')
const ensureLogIn = require('connect-ensure-login').ensureLoggedIn
const cookieParser = require('cookie-parser')
const app = express()
const port = 3000
const ensureLoggedIn = ensureLogIn('/not-logged-in')
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: false
}))
app.use(passport.authenticate('session'))
app.use(passport.initialize())
app.use(cookieParser())
app.get('/', (req, res) => {
res.json({ hello: 'world' })
})
app.all('/secure', ensureLoggedIn, (req, res) => res.json({ is: 'secure' }))
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
passport.use(new LocalStrategy({
usernameField: 'username',
passwordField: 'password',
session: true
},
function (username, password, cb) {
if (username !== 'hello' && password !== 'world') {
console.log('verify', username, password, 'fail')
return cb(null, false)
} else {
console.log('verify', username, password, 'pass')
return cb(null, { username: 'hello' })
}
}
))
passport.serializeUser(function (user, cb) {
process.nextTick(function () {
console.log('serializeUser', user)
cb(null, user)
})
})
passport.deserializeUser(function (user, cb) {
process.nextTick(function () {
console.log('deserializeUser', user)
return cb(null, user)
})
})
app.post('/logins',
passport.authenticate('local', {
// successReturnToOrRedirect: '/',
failureRedirect: '/login-fail',
failureMessage: true
}),
function (req, res) {
console.log('logged in', req.params, req.user)
// res.send('Logged in')
res.redirect('/secure')
}
)
I am using passport-cas to implement CAS 2.0 on my web server. I am trying to create a dummy environment where I just implement login functionality. It seems that the CAS login implementation is successful, I get redirected to the CAS login page but upon logging in the URL I am getting is http://localhost:3000/cas?ticket=ST-439273-mNA1Jeea4lEjK5vzJJuJmHHZ72w-vm-cas6prdapp-01 instead of redirecting to a page. I am not sure what is going on and can't find any similar answers online.
Here is my code containing the configuration of passport.
const app = express()
const CasStrategy = require('passport-cas').Strategy
const cors = require('cors')
const passport = require('passport')
const session = require('express-session')
const authRoute = require("./routes/auth.js");
app.use(session({
secret: "this is totally secret",
resave: false,
saveUninitialized: false,
}))
app.use(passport.initialize());
app.use(passport.session());
app.use(cors({
origin: "http://localhost:3000",
methods: "GET, POST,PUT, DELETE",
credentials: true,
}))
// app.use("/auth", authRoute)
passport.use(new CasStrategy({
version: 'CAS3.0',
ssoBaseURL: 'https://secure.its.yale.edu/cas',
serverBaseURL: 'http://localhost:3000/cas',
validateURL: '/validate'
}, function(profile, done) {
var login = profile.user;
User.findOne({login: login}, function (err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, {message: 'Unknown user'});
}
return done(null, user);
});
}));
passport.serializeUser(function (user, done) {
done(null, user.netId);
});
passport.deserializeUser(function (netId, done) {
done(null, {
netId,
});
});
const casLogin = function (
req, res, next
) {
passport.authenticate('cas', function (err, user, info) {
if (err) {
return next(err);
}
if (!user) {
req.session.messages = info.message;
return res.redirect('/check');
}
req.logIn(user, function (err) {
if (err) {
return next(err);
}
req.session.messages = '';
return res.redirect('/after');
});
})(req, res, next);
};
app.get("/cas", casLogin)
app.listen("5000", () =>{
console.log("Server is running on port 5000");
})
I'm working in devMode with angularjs and express-session with cors middleware and I run frontend from localhost:4200 and backend from localhost:8080
In login request I set user data in session and then when I call "/api/contacts", the session user data is undefined.
I tried to save session with session.save() but it does not work.
I noticed that between calls sessionID changes.
I searched for hours on google but I have not found any solution.
this is the frontend call to "/api/contacts"
this.http.get(environment.apiUrl + '/api/contacts', {
withCredentials: true,
})
this is part of server.js
app.use(cors({origin: [
"http://localhost:4200"
], credentials: true,
}));
let sess = session({
secret: 'my secret',
resave: false,
saveUninitialized: false,
store: new MemoryStore({
checkPeriod: 60000 * 5 // prune expired entries every 24h
}),
cookie: {
secure: app.get('env') === 'production'?true:false,
maxAge: 60000 * 5 ,
}
})
app.use(sess)
// Initialize the app.
var server = app.listen(process.env.PORT || 8080, function () {
});
const authMiddleware = (req, res, next) => {
// here req.session.user IS undefined
if(req.session && req.session.user) {
next();
} else {
res.status(403).send({
status: 403,
errorMessage: 'You must be logged in.'
});
}
};
app.get("/api/contacts", authMiddleware,(req, res) => {
// some code will run if authMiddleware pass
});
app.post('/api/login', validatePayloadMiddleware, (req, res) => {
if (req.body.username === "xx.xxxx#xxxx.xxx" && req.body.password === "xxxxxxx")
{
let user = {
id: req.sessionID,
username: req.body.username,
firstName: "Fabio",
lastName: "Spadaro",
};
req.session.user = user;
req.session.save((err) => {
console.log(err)
});
return res.status(200).json(user);
}
else
{
let body = {
error: true,
errorMessage: 'Permission denied!'
};
return res.status(403).json(body);
}
});
passport-github is having a hard time knowing the req.user. The following code worked a few minutes ago, now im getting this error
TypeError: Cannot read property 'id' of undefined
shows on this line
var token = jwt.sign({ id: req.user.id}, process.env.JWT_SECRET );
I don't have this issue with passport local strategy, and i do have a store configured. Could it be an issue with the serialization ?
Or maybe because im testing the route path so much that after a while the github api stops working ?
routes/users.js
router.get('/auth/github', passport.authenticate('github', {
session:true,
scope:[ 'id', 'profile']
}));
router.get('/auth/github/callback', (req, res, next) => {
passport.authenticate('github', (user) => {
// Successful authentication, redirect home.
var token = jwt.sign({ id: req.user.id}, process.env.JWT_SECRET );
// res.cookie("jwt", token, { expires: new Date(Date.now() + 10*1000*60*60*24)});
jwt.verify(token, process.env.JWT_SECRET, function(err, data){
console.log(err, data);
})
res.status(200).send({message:"github user signed in", auth: true});
// console.log(`frontid ${req.user.id}`)
// res.redirect('')
console.log('this works', token);
})(req, res, next);
});
passport-github.js
const passport = require("passport");
const GitHubStrategy = require('passport-github2').Strategy;
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
const models = require("../models/");
// passport.serializeUser((user, done) => {
// // push to session
// done(null, user.id);
// console.log(user.id)
// });
// passport.deserializeUser((id, done) => {
// models.User.findOne({
// where: {
// id,
// },
// }).then(user => done(null, user))
// .catch(done);
// });
passport.use(
new GitHubStrategy(
{
clientID: process.env.clientID,
clientSecret: process.env.secret,
callbackURL: 'http://127.0.0.1:8000/api/users/auth/github/callback',
passReqToCallback: true,
profileFields: ['id', 'login']
},
(req, accessToken, refreshToken, profile, done) => {
const { id, login, email} = profile._json;
console.log(`backbro ${id}`);
// console.log(req)
models.User.find({
where:{
id: id
}
}).then( user => {
// if user is found
if(user){
return done(null, user)
}
// else create new user
else{
models.User.create({
id: id,
username:login,
email: email,
createdAt: Date.now()
}).then( user => {
console.log('github user created');
return done(null, user);
})
}
})
}
)
);
passport.serializeUser((user, done) => {
// push to session
done(null, user.id);
});
passport.deserializeUser((userId, done) => {
// console.log('calling deserial' + userId);
// // TODO: findByPk syntax? findById deprecated? Try later after sucessfully record data in DB
models.User
.find({ where: { id: userId } })
.then(function(user){
// console.log(user);
return done(null, userId);
}).catch(function(err){
done(err, null);
});
// return done(null, id);
});
module.exports = passport;
routes/current_user
router.get("/current_user", (req, res) => {
if(req.user){
res.status(200).send({ user: req.user});
} else {
res.json({ user:null})
}
});
app.js
var sequelize = new Sequelize(
process.env.POSTGRES_DB,
process.env.POSTGRES_USER,
process.env.POSTGRES_PASSWORD,{
"dialect": "sqlite",
"storage": "./session.sqlite"
});
myStore = new SequelizeStore({
db:sequelize,
})
if (!process.env.PORT) {
require('dotenv').config()
}
// console.log(process.env.DATABASE_URL);
if (!process.env.PORT) {
console.log('[api][port] 8000 set as default')
console.log('[api][header] Access-Control-Allow-Origin: * set as default')
} else {
console.log('[api][node] Loaded ENV vars from .env file')
console.log(`[api][port] ${process.env.PORT}`)
console.log(`[api][header] Access-Control-Allow-Origin: ${process.env.ALLOW_ORIGIN}`)
}
app.use(logger('dev'));
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'build')));
app.use(cookieParser());
// We need a store in order to save sessions, instead of the sessions clearing out on us :)
app.use(session({
store: myStore,
saveUninitialized: false,
resave:false,
cookie: { maxAge: 30 * 24 * 60 * 60 * 1000 }, // 30 days
secret : process.env.JWT_SECRET,
}));
myStore.sync();
require('./config/passport')(passport); // PASSPORT Init
require('./config/passport-github'); // PASSPORT Init
app.use(passport.initialize());
app.use(passport.session());
app.use(bodyParser.urlencoded({ extended:false}));
app.use(bodyParser.json());
app.use(function(req, res, next) {
res.locals.user = req.user; // This is the important line
// req.session.user = user
console.log(res.locals.user);
next();
});
// this code may be useless or useful, still trying to understand cors.
app.use((req, res, next) => {
const { headers } = req;
res.header('Access-Control-Allow-Origin', headers.origin);
res.header('Access-Control-Allow-Headers', headers);
res.header('Access-Control-Allow-Credentials', true);
next();
});
app.use(cors({
origin: process.env.ALLOW_ORIGIN,
credentials: true,
allowedHeaders: 'X-Requested-With, Content-Type, Authorization',
methods: 'GET, POST, PATCH, PUT, POST, DELETE, OPTIONS'
}))
app.use('/api/users', userRoute );
app.use('/api/posts', postRoute );
// In order to use REACT + EXPRESS we need the following code, alone with a build
// in the client folder we run a npm run build in the client folder then it is referred
// in the following code.
app.use(express.static(path.join(__dirname, 'client/build')));
if(process.env.NODE_ENV === 'production') {
app.use(express.static(path.join(__dirname, 'client/build')));
//
app.get('*', (req, res) => {
res.sendfile(path.join(__dirname = 'client/build/index.html'));
})
}
//build mode
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname+'/client/public/index.html'));
})
models.sequelize.sync().then(function() {
app.listen(PORT, host, () => {
console.log('[api][listen] http://localhost:' + PORT)
})
})
I am currently using the create-react-app boiler plate and have been attempting to add auth. I am using axios as my promise based HTTP libray with React.js. I have been using node with express, express-session, passport and passport-local on the backend.
Here is my server.js file with some exlusions:
const express = require('express');
const mysql = require('mysql');
const app = express();
const cors = require('cors');
const session = require('express-session');
const passport = require('passport');
const morgan = require('morgan');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const LocalStrategy = require('passport-local').Strategy;
// Express only serves static assets in production
if (process.env.NODE_ENV === 'production') {
app.use(express.static('client/build'));
}
app.set('port', (process.env.PORT || 3001));
app.use(cors({
credentials: true,
origin: 'http://localhost:3000'
}));
app.use(morgan('dev'));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cookieParser());
app.use(session({
secret: 'topsecretpassword',
resave: true,
saveUninitialized: false,
cookie: {
path: '/',
originalMaxAge: 1000 * 60 * 60 * 24,
httpOnly: true,
secure: false
}
}));
app.use(passport.initialize());
app.use(passport.session());
// Setup Database connection
const connection = mysql.createConnection({
host : 'localhost',
user : 'root',
password : '',
database : 'mvy_db'
});
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(user, done) {
connection.query('SELECT * FROM users WHERE id=?', user, function(err, userId) {
if (err) {
res.status(400).json({
error: 'Database Error',
id: userId[0]
});
}
done(err, userId[0]);
});
});
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
},
function(email, password, done) {
connection.query('SELECT * FROM users WHERE email=?', email, function(err, user) {
if (err) {
return done(err);
}
if (!user.length) {
return done(null, false, { message: 'Incorrect email.' });
}
if (user[0].password !== password) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user[0]);
});
}
));
app.post('/signin', passport.authenticate('local'), function(req, res) {
console.log(req.session);
return res.send('login success!');
});
function isAuthenticated (req,res,next){
console.log(req.session);
if(req.session.passport.user)
return next();
else
return res.status(401).json({
error: 'User not authenticated'
})
}
app.get('/checkauth', isAuthenticated, function(req,res) {
res.status(200).json({
status: 'User Authenticated!'
});
})
app.get('/signout', function(req,res) {
req.session.destroy();
res.status(200).json({ success: 'successfully signed out' });
})
Using postman (and even on the browser), I am able to successfully login and the following is held in the req.session object :
cookie:
{ path: '/',
_expires: null,
originalMaxAge: 86400000,
httpOnly: true,
secure: false },
passport: { user: 1 } }
my login request using axios:
return axios.post(ROOT_URL + 'signin', {
email: e.target.email.value,
password: e.target.password.value
}).then((response) => {
if (response.status === 200) {
console.log(response);
}
})
My checkAuth request using axios (this is where I get a 500 error returned):
axios.get(ROOT_URL + 'checkauth', { withCredentials: true })
.then((response) => {
if (response.status === 200) {
return true;
} else {
return false;
}
});
The req.session object after checking authentication before the error message, note that the passport object doesn't exist anymore:
Session {
cookie:
{ path: '/',
_expires: null,
originalMaxAge: 86400000,
httpOnly: true,
secure: false } }
This is the error message I get on the console when I attempt to check that the user is authorized:
TypeError: Cannot read property 'user' of undefined
at isAuthenticated (/server.js:94:26)
I've been banging my head for hours, trying to resolve this issue. I thought it might have something to do with CORS, but after hours of playing around with it that doesn't seem to be the case. It's still plausible that it's a CORS issue, but what's really flustering me is that it works full well with Postman but not on my Chrome browser. Any help is appreciated!
Alright, so I found the solution to my problem. It appeared to be an issue with axios and the configuration of my get requests. For some reason, using the structure axios.get(URL) .then(response) doesn't work with the withCredentials property.
Instead, I had to send my request as:
axios(ROOT_URL + 'checkauth', {
method: 'get',
withCredentials: true
})
.then((response) => {
if (response.status === 200) {
return true;
} else {
return false;
}
});
Oh because I forgot that axious doesn’t send credentials by default I had to stick with jwt and completely removed session.
You can define an instance of axious which will allow you to make requests much more simply
const $axios = axios.create({
baseURL: 'https://some-domain.com/api/',
withCredentials: true
});