I'm developing a register/login website which includes all features to make it work in an efficient and secure way using reactJS, NodeJS and Mysql.
Everything was working fine until I used express-session. In fact, when a user logs in, he will be redirected to a home page (obviously a session will be created) but when the user refreshes the page, It is expected to stay on the home page but the behavior I got is losing the session, thus being redirected to login page.
I looked for a fix and I already tried enabling credentials with Axios in the frontEnd and Cors in the backEnd but the problem is persisting.
This is my code:
server.js
const express = require('express');
const app = express();
const mysql = require('mysql2');
const cors = require('cors');
const validator = require('validator');
const {body, validationResult} = require('express-validator');
const session = require('express-session');
const cookieParser = require('cookie-parser');
app.use(express.json());
app.use(cors({
origin: ['http://localhost:3000'],
methods: ['GET', 'POST'],
credentials: true,
}
));
app.use(express.urlencoded({extended: true}));
app.use(cookieParser());
app.use(session({
name: 'session',
secret: 'crud',
resave: false,
saveUninitialized: false,
cookie: {
expires: 60 * 30,
sameSite: 'strict',
}
}
app.post('/login', (req, res) => {
const mail = validator.escape(req.body.mail);
const pass = validator.escape(req.body.pass);
const sqlSelect = 'SELECT * FROM login WHERE mail = ? AND pass = ?';
db.query(sqlSelect, [mail, pass], (err, result) => {
if (err) {
console.log(err);
}
if (result.length > 0) {
req.session.user = result;
req.session.loggedIn = true;
console.log(req.session.user);
res.send({message: 'success', session: req.session});
}
else {
res.send({message: 'Wrong combination Email/Password !'});
}
})
});
app.get('/login', (req, res) => {
console.log(req.session.user);
if (req.session.user){
res.send({
session: req.session,
message: 'logged'
});
}
else {
res.send({
message: 'Not logged'
});
}
});
app.js (login page)
Axios.defaults.withCredentials = true;
const onSubmit = () => {
Axios.post('http://localhost:9001/login', {
mail,
pass,
}).then((response) => {
console.log(response.data.message);
if (response.data.message === 'success') {
history.push('/home');
}
else {
setMessage(response.data.message);
}
});
};
home.js
export default function Home() {
const [user, setUser] = useState('');
const history = useHistory();
useEffect(() => {
Axios.get('http://localhost:9001/login', {withCredentials: true}).then((response) => {
console.log(response.data.message);
if (response.data.message === 'logged'){
setUser(response.data.session.user[0].mail);
}
else {
history.push('/');
}
})
//eslint-disable-next-line
}, []);
return (
<div>
<p>{user}</p>
</div>
)
}
I hope someone is able to suggest some fix to this. I know I can use localStorage but I want to use the session instead.
Related
When i tried to use it with react app i can not reach the value. I use sequelize orm and mysql database. I want to send the session information to the react side when the user logs in
app.js file:
const express = require('express')
const dotenv = require("dotenv")
const app = express()
const body_parser = require("body-parser");
const cookieParser = require("cookie-parser")
const session = require('express-session')
const cors = require("cors")
dotenv.config()
app.use(cors())
const SequelizeStore = require("connect-session-sequelize")(session.Store);
const adminRoute = require("./routes/admin")
const locals = require("./middlewares/locals")
app.use(cookieParser())
app.use(express.urlencoded({ extended: false }))
app.use(body_parser.json())
const sequelize = require("./data/db")
app.use(session({
secret: "hello world",
resave: false,
saveUninitialized: false,
cookie: {
maxAge: 1000 * 60 * 60 * 24
},
store: new SequelizeStore({
db: sequelize
})
}))
app.use(locals);
app.use(adminRoute)
const dummy = require("./data/dummy-data")
async function x() {
// await sequelize.sync({ force: true })
// await dummy()
}
x()
app.listen(process.env.PORT, () => {
console.log(`Server running on Port: ${process.env.PORT}`)
})
locals.js file:
module.exports = (req, res, next) => {
res.locals.auth = req.session.auth
console.log("auth : ", res.locals.auth)
next();
}
admin.js routes:
router.get("/api/session", async (req, res) => {
res.json({ isAuth: res.locals.isAuth });
});
router.post("/login", async (req, res) => {
const { email, password } = req.body
console.log(req.body)
try {
const user = await Users.findOne({
where: {
email: email
}
})
if (!user) {
return res.status(404)
}
const match = await bcrypt.compare(password, user.password)
console.log("match:", match)
if (match) {
req.session.isAuth = 1
return res.status(200).send({ message: "login successful" })
}
else {
req.session.isAuth = 0
return res.status(404)
}
}
catch (err) {
console.log(err)
}
})
React Login.js:
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const login = async (e) => {
e.preventDefault()
const response = await axios.post("http://localhost:5000/login", { email: email, password: password })
console.log(response)
if(response.status === 200){
const response2 = await axios.get("http://localhost:5000/api/session")
console.log(response2)
}
}
Login.js first response = data: {message: 'login successful'}
Login.js second response = data: {}
If i try to react http://localhost:5000/login by hand on the browser auth sets as true. But when i refresh react side it sets undefined again.
I am trying to send an axios request from the vue front end to node js backend with sessionID to handle sessions for admins.
this is a login sample to login admins
axios.defaults.withCredentials = true;
const submit = async (data) => {
const { email, password } = data;
const url = "http://localhost:5000/api/v1/users/loginAdmin";
try {
const res = await axios.post(url, {
email,
password,
});
error.value = undefined;
console.log(res.data);
const loginIng = useLogin();
loginIng.logIn(res.data);
router.push("/");
} catch (err) {
console.log(err);
error.value = err.response.data.message;
}
};
after the login is successfully done and that what happened with me, the user is pushed to the root at / and beforeMount the component, there a check to validate the admin credintials in the backend, like this
axios.defaults.withCredentials = true;
setup() {
const login = useLogin();
const router = useRouter();
onBeforeMount(() => {
axios
.post("http://localhost:5000/api/v1/users/validateAdmin")
.then((res) => {
login.logIn(res.data);
})
.catch((err) => {
console.log(err);
router.push("/login");
});
});
return {};
},
In the backend in the app.js, there is a session and cors policy to allow origin from the front end, and session is stored in sqlite3 like the following.
app.use(
session({
store: new KnexSessionStore({
knex: db,
tablename: "sessions",
}),
secret: process.env.SECRET,
resave: false,
saveUninitialized: false,
cookie: {
maxAge: 1000 * 60 * 60 * 24,
secure: process.env.NODE_ENV === "production",
},
})
);
in the login admin the session is saving a user with his credintials
like this
const loginAdmin = async (req, res) => {
req.session.user = admin;
}
and this is the ValidateAdmin endpoint sample
const validateAdmin = async (req, res) => {
const userSession = req.session;
const user = userSession.user;
}
the data is being saved to the sqlite3 file in the table, but each time the user visits the endpoint, there is a new id generated for him while i do not need that to happen
What I have already tried:
setting rolling: false, in the session
adding
app.use((req, res, next) => {
req.session.touch();
next();
});
to the app.js
Thanks in advance
I have an express server backend where I am using sessions to keep track of user when they are logged in or not, and using passportJS for authentication. ReactJS for the frontend. React runs on default port 3000 and express app runs on 5500.
Right now, whenever I use a post request to /api/login it logs in the user and sends 200 ok and creates a session with passport user object and then redirects to profile page, I have another GET request that requests to get the user data. The profile page sends a GET request to /api/profile which checks if the user is authenticated and if yes sends back user data to populate profile page with user details. But right now, every time the profile page sends a GET request back to the server it creates a new session overwriting the sessions created in /api/login and loses user data showing the user is not Authenticated.
Here is my server.js file
const express = require("express");
const MongoStore = require("connect-mongo");
const indexRouter = require("./routes/index");
const app = express();
const cors = require("cors");
const bodyparser = require("body-parser");
const mongoose = require("mongoose");
const dotenv = require("dotenv");
const passport = require("./Authentication/passportConfig");
const session = require("express-session");
const cookieParser = require("cookie-parser");
dotenv.config({ path: "../app/Private/.env" });
let port = process.env.PORT;
app.use(bodyparser.json());
app.use(bodyparser.urlencoded({ extended: true }));
app.use(
cors({
origin: "http://localhost:3000",
methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
credentials: true,
})
);
app.use(cookieParser("secret"));
app.use(
session({
secret: "secret",
store: MongoStore.create({ mongoUrl: process.env.ADMIN_URI }),
resave: false,
saveUninitialized: false,
cookie: { maxAge: 30 * 24 * 60 * 60 * 1000, httpOnly: true },
})
);
app.use(passport.initialize());
app.use(passport.session());
//Database connection process
mongoose
.connect(process.env.ADMIN_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => console.log("connected to database"))
.catch(console.error);
//Route creation
app.use("/api", indexRouter);
app.listen(port, () => console.log("Server connected and running: " + port));
and here are the two routes in my indexRouter
const express = require("express");
const router = express.Router();
const passport = require("../Authentication/passportConfig");
const dotenv = require("dotenv");
dotenv.config({ path: "../app/Private/.env" });
const nodemailer = require("nodemailer");
const User = require("../model/user");
const hash = require("../Authentication/passwordHash");
const validator = require("node-email-validation");
const Appointment = require("../model/appointment");
router.post("/login", passport.authenticate("local"), (req, res) => {
var time = new Date(Date.now());
console.log(
"Session ID before # /login route: " +
time.toLocaleTimeString() +
": " +
req.sessionID
);
if (req.user) {
console.log("Session ID after # /login route: " + req.sessionID);
console.log("User is logged in: " + req.user.username);
console.log("User is Authenticated: " + req.isAuthenticated());
res.status(200).json({ message: "Login successful" });
} else {
res.send(400).json({ message: "User not found" });
}
});
router.get("/profile", function (req, res) {
var time = new Date(Date.now());
console.log(
"Session ID # /profile route: " +
time.toLocaleTimeString() +
": " +
req.sessionID
);
if (req.isAuthenticated()) {
console.log(req.isAuthenticated());
console.log("user: " + req.user.username);
return res.status(200).json(req.user);
num = num + 1;
} else {
console.log("else Statement");
return res.status(302).json({ message: "User not found" });
}
});
Here is the output from console logging session at the different routes
Session ID before # /login route: 5:41:07 PM: 8Z1RebDKynOESWoQ5ecA3EecOojmthL9
Session ID after # /login route: 8Z1RebDKynOESWoQ5ecA3EecOojmthL9
User is logged in: alex#s3v.us
User is Authenticated: true
Session ID # /profile route: 5:41:09 PM: -peiT761fG_ZY9gLgWFpqeUE6hTZmRQV
else Statement
Session ID # /profile route: 5:41:09 PM: kgXFngZMamCqh4mapynMQQN7cgL9Er-1
else Statement
Session ID # /profile route: 5:41:09 PM: zCZTtlsK-g0MSvo9j5ZbAAs35vaXtsnO
else Statement
Session ID # /profile route: 5:41:09 PM: Oq8J-s08m66P5JuuTO1RI5ZIy8oOeXRD
else Statement
Here is the ReactJS frontend calls in Login page
const config = {
headers: {
"Access-Control-Allow-Origin": "http://localhost:3000",
"Access-Control-Allow-Methods": "GET,PUT,POST,DELETE,PATCH,OPTIONS",
"Access-Control-Allow-Credentials": "true",
},
};
const isLoggedIn = () => {
axios
.get("http://localhost:5500/api/profile", config)
.then((res) => {
console.log("Is user logged in status: " + res.status);
console.log(res.data);
if (res.status === 200) {
window.location.href = "/MyProfile";
}
})
.catch((err) => {
console.log("err" + err);
});
};
/*
on Submit function for the login page
*/
const onSubmitLogIn = (e) => {
e.preventDefault();
const user = {
username: username.toString().toLowerCase(),
password: password,
};
axios
.post("http://localhost:5500/api/login", user, config)
.then((res) => {
console.log(res.data);
if (res.status === 200) {
setSuccessMessage(true);
setTimeout(() => {
window.location.href = "/MyProfile";
}, 2000);
}
})
.catch((err) => {
setErrormessage(true);
});
};
Here is the hook call and get request in profile.jsx
useEffect(() => {
fetchUserData();
}, []);
const fetchUserData = () => {
axios
.get("http://localhost:5500/api/profile", config)
.then((res) => {
console.log("res: " + res);
console.log("User: " + res.data);
setUserData(res.data);
})
.catch((err) => {
console.log(err);
console.log(err.response);
});
};
I don't understand where I am going wrong and why the call in profile creates a new session.
Add the {withCredentials: true} option to your Axios call so that Axios will send cookies (and thus the sessions cookie) with your request.
const fetchUserData = () => {
axios
.get("http://localhost:5500/api/profile", {withCredentials: true})
.then((res) => {
console.log("res: " + res);
console.log("User: " + res.data);
setUserData(res.data);
})
.catch((err) => {
console.log(err);
console.log(err.response);
});
};
When cookies are not sent with the request, then your session cookie is not present and the backend creates a new session everytime.
I have a nodejs backend with sessions made using express-session npm module. I want to test the sessions using postman.
What I want is a user should be allowed to access the users list via the /getUsers route only if he is currently in a session. But what is happening is when I test the backend using postman even after logging in the user is unable to access the users using the /getUsers route. Is this something to do with postman?
Here is my app.js
const express = require("express");
const app = express();
const authRoutes=require('./routes/auth');
const mongoose=require('mongoose');
const bodyParser = require("body-parser");
require("dotenv").config();
const nodemailer = require("nodemailer");
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
const cors = require('cors');
app.use(cors({
origin:['http://localhost:8080'],
methods:['GET','POST'],
credentials: true // enable set cookie
}));
app.use(
// Creates a session middleware with given options.
session({
name: 'sid',
saveUninitialized: false,
resave: false,
secret: 'sssh, quiet! it\'s a secret!',
cookie: {
httpOnly: true,
maxAge: 1000 * 60 * 60 * 2,
sameSite: true,
secure: true
}
})
);
mongoose.connect(process.env.LOCAL_MONGO_URI,{useNewUrlParser:true},function (err) {
if (err) throw err
console.log("Connected to local mongo db database");
});
app.get("/",(req,res)=> {console.log("A request was made to /")
console.log("/GET called");
});
app.use(bodyParser.json());
app.use("/",authRoutes);
const port = process.env.PORT||8080
app.listen(port,()=> {
console.log("Hello world");
})
Here is my routes/auth.js:
const express = require("express");
const {
signup,login,verifyemail,requiresLogin,getUsers,logout
} = require("../handler/auth")
const router = express.Router();
router.post("/signup",signup);
router.post("/login",login);
router.get("/verifyemail/:token",verifyemail);
router.get("/getUsers",requiresLogin,getUsers);
router.get("/getUsers",requiresLogin,getUsers);
router.get("/logout",requiresLogin,logout);
module.exports=router;
Here is my handler/auth.js
const User = require("../models/user");
const bcrypt = require('bcrypt');
const crypto = require('crypto');
exports.signup = async (req, res) => {
const email = req.body.email;
User.findOne({email},function(err,user){
if(err) return res.status(500).json({message:err.message});
else if(user) return res.status(403).json({"message":"User exists"});
const password = req.body.password;
const name = req.body.name;
bcrypt.hash(password, 10)
.then(async function(hashed_password) {
const user = await new User({email,name,hashed_password});
user.emailVerificationToken = crypto.randomBytes(20).toString('hex');
user.emailVerificationTokenExpires = Date.now() + 3600000*24;
await user.save(function(err) {
if(!err) {
const resetURL = `http://${req.headers.host}/verifyemail/${user.emailVerificationToken}`;
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
const msg = {
from: 'admin#pinclone.com',
to: email,
subject: 'Email verification link',
html: `Verify your email here to login to your account`,
};
sgMail.send(msg);
return res.json({message:"verify email address to login"});
}
return res.status(500).send({ message: err.message });
});
})
.catch(function(error){
res.status(500).send({message:error.message});
});
});
};
exports.login = (req,res) => {
const email = req.body.email;
const password = req.body.password;
User.findOne({email},function(err,user) {
if(err) return res.status(500).json({message:err.message});
if(!user) return res.status(403).json({"message":"User does not exists"});
bcrypt.compare(password,user.hashed_password,(err,result) => {
if(result) {
if(user.isVerified)
return res.status(200).json({"message":"successfully logged in"});
else
return res.status(403).json({"message":"user is not verified"});
}
else return res.status(403).json({message: "email address password do not match"});
});
});
};
exports.verifyemail = async (req,res) => {
User.findOneAndUpdate({emailVerificationToken: req.params.token,emailVerificationTokenExpires: { $gt: Date.now() }}, {$set:{isVerified:true}}, {new: true}, (err, user) => {
if (err) {
res.status(403).send({message:"Link invalid or expired"});
// res.status(500).send({message:"Something wrong when updating data!"});
}
if(user) {
res.status(200).send({"message":"email verification successful you can login now!"});
}
});
};
exports.requiresLogin = (req, res, next) => {
if (req.cookies.sid) {
return next();
} else {
var err = new Error('You must be logged in to view this page.');
err.status = 401;
return next(err);
}
};
exports.logout = (req, res) => {
res.clearCookie('sid');
res.send("logout success");
};
exports.getUsers = (req,res) => {
User.find({},function(err,users){
res.send(users);
});
};
You can set session in tab Header with your key.
You can read https://www.toolsqa.com/postman/sessions-in-postman/ for more detail.
You should read at How to use Sessions in Postman? part.
I've got some issues with authentication (using cookies and session) and my electron-app
The use case:
User logs in
Session created and cookie is stored. (by app-bl module)
I read about electron-session and electron-cookies (https://electronjs.org/docs/all?query=coo#class-cookies) but nothing works.
Application structure:
electron-app
---express-app
------app-bl
------react-client
Electron version: 3.0.13
I used this to use express within electron:
https://github.com/frankhale/electron-with-express
It seems like electrons main process doesn't know about cookies created by the rendered process.
electron/main.js:
const electron = require('electron');
const { app, BrowserWindow, session } = electron
let mainWindow;
function createWindow() {
const screenElectron = electron.screen;
mainWindow = new BrowserWindow({
show: false,
autoHideMenuBar: true,
icon: `${__dirname}/assets/icon.ico`
});
mainWindow.webContents.openDevTools();
mainWindow.loadURL(`file://${__dirname}/index.html`);
mainWindow.on("close", () => {
mainWindow.webContents.send("stop-server");
});
mainWindow.once('ready-to-show', () => {
mainWindow.show()
})
mainWindow.on("closed", () => {
mainWindow = null;
});
}
express-app/index.js:
const ev = require('express-validation');
const Path = require('path')
const Express = require('express')
const BodyParser = require('body-parser')
const CookieParser = require('cookie-parser');
const Session = require('express-session');
const App = require('./app/index.js')
// Init server
const express = Express()
const router = Express.Router()
const port = parseInt(process.argv[2]) || process.env.PORT || 5001
const ip = "0.0.0.0"
express.use(BodyParser.urlencoded({ extended: true }))
express.use(BodyParser.json())
express.use(CookieParser())
express.use(Session({
key: 'sessionId',
secret: 'key',
resave: false,
saveUninitialized: false,
cookie: {
expires: 600000
}
}))
// Init Application
const app = App({ express, router })
// Static content
express.use(Express.static(Path.join(__dirname, './client/dist')))
express.use('/*', Express.static(Path.join(__dirname, './client/dist/index.html')))
// Error handler
express.use(function (err, req, res, next) {
console.log(err)
if (err instanceof ev.ValidationError) {
return res.status(err.status).json({
status: err.status,
message: err.statusText
});
}
return res.status(err.status).json({
status: err.status,
message: err.message
});
});
(async () => {
try {
await app.init()
const server = await app.start(ip, port);
console.log("Server started http://%s:%s", ip, port)
} catch (e) {
console.log(e);
process.exit(1);
}
})()
And this is how I'm creating the session after successful login in app-bl module:
async function loginHandler(req, res, next) {
const username = req.body.username
const password = req.body.password
try {
const user = await authService.login(username, password)
req.session.userId = user.id;
res.json({ user })
} catch (error) {
error.status = 500
next(error)
}
}
I managed to create cookies inside main process and I can see then using console.log, but nothing is showing inside devTools, I tried this code:
const mainSession = mainWindow.webContents.session
const cookie = {
url: 'http://localhost:8000',
name: 'sessionId',
domain: 'localhost',
expirationDate: 99999999999999
}
mainSession.cookies.set(cookie, (error) => {
if (error) {
console.error(error)
}
})
mainSession.cookies.get({}, (error, cookies) => {
console.log(cookies)
})
I have the feeling I'm missing something here.
This package works: https://github.com/heap/electron-cookies
It seems to use Local Storage to emulate cookies. So you can read/write cookies using normal JavaScript syntax, and hopefully your application won't know the difference.