How do i print out flash messages in handlebars. Like i want to print out a helper message to the user if they enter a wrong login.
Routes:
router.post('/register-user', async (req, res, next) => {
let newUser = req.body
try {
if (await userCheck.userCheck(newUser)) {
const userExists = await User.exists({ username: req.body.username })
if (userExists) {
req.session.email = req.body.username;
res.redirect('/apikey')
console.log("User exists")
} else {
try {
req.session.email = req.body.username;
await userFacade.addUser(newUser)
console.log("New user")
res.redirect('/apikey')
} catch (err) {
console.log(err)
}
}
} else {
req.flash("error", "Something went wrong during login, please try again.");
res.redirect('/')
}
} catch (error) {
console.log(error)
}
})
I have the flash message "error", but how do i show that on the page only if its set? Like if u were to use useStates in React.
I tried something like this:
{{#if error}}
{{error}}
{{/if}}
App.js
//Express session
app.use(session({
secret: process.env.SESSION_SECRET,
saveUninitialized: true,
resave: true
}));
//Flash
app.use(flash());
//Routes
app.use('/', require('./routes/index'))
In order to render the value you have saved to the flash store you would need to retrieve it from the store by calling req.flash using the same key that you used to save the value: req.flash('error'). You would then need to assign the returned value to a member of the data object you are passing to your render call in your GET handler for the / path - as this is the path you redirect your users to when you call res.redirect('/').
Your GET handle for / may become something like the following:
app.get('/', (req, res) => {
res.render('index', { error: req.flash('error') });
});
Note: The result of calling req.flash('error') is an array. Your template will work because Handlebars stringifies array values, but {{#each error}}{{this}}{{/each}} would produce the same result when error is an array with a single element. If error were to have multiple elements, the {{error}} in your template would print all elements joined with commas, as in error 1,error 2,error 3.
Related
I'm currently creating an application in React/Express and I'm learning how to create sessions. I'm using express-session as it's what everyone recommends but I have unexpected behaviors.
In my route post, the route used during the connection, I try to create a new session for the user but it does not seem to work (no cookie and the session is not created) while my console.log returns the expected information.
router.post('/login', async (req, res) => {
const user = await Users.findOne({where: {Email: req.body.Email}})
if (!user) res.json({error: "User doesn't exist"})
bcrypt.compare(req.body.Password, user.Password).then((match) => {
if (!match) res.json({error: "Wrong password"})
req.session.user = user.dataValues
console.log(req.session)
})
})
In my get route, which is called at every refresh of the page, I realize that the session is empty and a new cookie is created (I don't really know why).
router.get('/login', async (req, res) => {
console.log(req.session)
if (req.session.user) {
res.send({loggedIn: true, user: req.session.user})
} else {
res.send({ loggedIn: false})
}
})
Here is how I set up express-session as well as cors (I read that the problem could come from there but all seems correct).
app.use(cors({
origin: ["http://localhost:3000"],
methods: ["GET", "POST"],
credentials: true //permet d'activer les cookies
}))
app.use(session({
key: "userId",
secret: "foo",
resave: false,
saveUninitialised: true,
cookie: {
expires: 60 * 60 * 24
},
}))
I also read that the problem could come from the API call, I use Axios and I was careful to add the line Axios.defaults.withCredentials = true before the call.
Your router.post("/login", ...) route never sends any response back to the client. An express session works by establishing a cookie with the browser that the browser will send back on future requests. That cookie contains an encrypted session key that is the magic sauce that makes the session possible. When you don't send any response back from the /login POST, then that cookie never gets back to the browser and thus the session cookie can't be sent back on future requests and thus the session does not work.
Instead, the next request coming from the browser will not have a session cookie and thus Express will try to create yet another new empty session.
To fix that part of the issue, send a response back from your POST request:
router.post('/login', async (req, res) => {
const user = await Users.findOne({where: {Email: req.body.Email}})
if (!user) res.json({error: "User doesn't exist"})
bcrypt.compare(req.body.Password, user.Password).then((match) => {
if (!match) res.json({error: "Wrong password"})
req.session.user = user.dataValues;
console.log(req.session)
res.send("some response"); // <== send some response here
}).catch(err => {
// some error handling here
console.log(err);
res.sendStatus(500);
});
});
For, more complete and centralized error handling where you use http status to reflect actual errors, you can do something like this:
class myError extends Error {
constructor(message, status) {
super(message);
this.status = status;
}
}
router.post('/login', async (req, res) => {
try {
const user = await Users.findOne({where: {Email: req.body.Email}})
if (!user) throw new MyError("User doesn't exist", 404) ;
const match = await bcrypt.compare(req.body.Password, user.Password);
if (!match) throw new MyError("Wrong password", 401);
req.session.user = user.dataValues;
console.log(req.session);
res.json({loggedIn: true});
} catch(e) {
const status = e.status || 500;
res.status(status).json({error: e.message});
}
});
Note, I've stopped mixing await with .then() which is not considered good style and then used try/catch and throw to integrate the more comprehensive error handling into one spot.
When I login, I'd like to be able to display the name of the user as a link in my index.jade file, but nothing shows up. I've tried both req.body.username, and req.session, and neither of them have worked. Here's the login controller:
const login = (req, res, next) => {
var username = req.body.username
var password = req.body.password
User.findOne({$or: [{username:username}, {email:username}]})
.then(user => {
if(user) {
bcrypt.compare(password, user.password, function(err, result) {
if(err) {
res.json({
error: err
})
}
if(result) {
//Successful Login
let token = jwt.sign({name: user.name}, 'verySecretValue', {expiresIn: '1h'})
res.redirect('/')
} else {
res.json({
message: 'Password does not match!'
})
}
})
} else {
res.json({
message: 'No user found!'
})
}
})
}
Here's my Routing to the homepage:
var express = require('express');
var router = express.Router();
var mongoose = require('mongoose');
/* GET home page. */
router.get('/', function(req, res, next) {
console.log(req.query)
res.render('index', { title: 'Portfolio', name:req.params.name });
});
module.exports = router;
And a snippet from my index.jade file where I want to insert the data:
extends layout
block content
body
// Topbar Start
.container-fluid
.row.bg-secondary.py-2.px-xl-5
.col-lg-6.d-none.d-lg-block
.d-inline-flex.align-items-center
a.text-dark(href='') **#{req.body.username}**
span.text-muted.px-2 |
a.text-dark(href='') Help
span.text-muted.px-2 |
a.text-dark(href='') Support
First, some comments on your code.
req.params.name is not in your route for /. The use of req.params is for managing a parameterized url, example if you have router.get("/getprofile/:name");, then a request url of /getprofile/peter will return "peter" in req.params.name.
req.body is present in a Http post request containing the Html control values inside the <form> in the submitted page. Anyway, you did not pass the req object to the view page in the render method, so it is not available in index.jade.
Solution:
What you need is to persist the user information across pages. Inside your login code, after the user is securely authenticated, you can use (signed) cookies or session variables to set a user object to contain your user details:
if(result) {
//Successful Login
let token = jwt.sign({name: user.name}, 'verySecretValue', {expiresIn: '1h'})
res.session.user = { name: user.name, email: "..." };
res.redirect('/')
}
Then you can pass to your view page with something like:
res.render("index", { user: req.session.user });
And in your view page:
a.text-dark(href='') #{user.namme}
Note: you have to first set up express-session as described in the above link.
the problem is when i log req.session.user i get the user results not problem, but when i try to log socket.request.session i get an empty session without the user data, even if i use socket.request.session.user i get user is undefined. whats the problem with it? i really need some help its for my college project which is in the next week
i've followed the docs on socket.io page : https://socket.io/how-to/use-with-express-session
but it doesn't seem like its sharing my session to the socket, im trying to access user data on socket connection
This is my session/socket.io setup
const session = require("express-session");
const sessionMiddleware = session({
secret: "testing",
resave: false,
saveUninitialized: false
});
const wrap = middleware => (socket, next) => middleware(socket.request, {}, next);
module.exports = {sessionMiddleware, wrap};
io.use(wrap(sessionMiddleware));
io.on("connection", (socket) => {
console.log(socket.request.session)
// On new post
socket.on("newPost", (post) => {
console.log(post)
})
console.log(socket.id)
});
and this is my login setup
router.post("/login", async (req, res) => {
const data = {
username: req.body.username.toString(),
password: req.body.password.toString()
}
console.log(data)
if(data.username === "") {
res.json({
status: "error",
message: "Username is required"
})
return;
}
if(data.password === "") {
res.json({
status: "error",
message: "Password is required"
})
return;
}
let user = await new userClass();
if(await user.login(data.username, data.password)) {
req.session.user = await user.getUser();
console.log(req.session.user)
res.redirect("/");
return
} else {
res.redirect("/entry?error=username or password is incorrect");
return;
}
})
I'm using the express-session package and I want to change the variable "_id" in the session.
Here my session init
app.use(session({
secret: "secretshhhhhh",
resave: true,
saveUninitialized: false,
}))
After the login page I try to store the id with these few lines:
req.session._id = user._id.toString()
req.session.save(function (err) {
req.session.save(function (err) {
console.log(req.session)
})
})
The console.log print the session with the id, but when I try to get the _id in an other page express send me back a null object. Here an exemple of printing session without the _id
return res.status(200).send(req.session);
I tried many methods but none of these worked for me.
EDIT:
Here my whole function to put it in session
module.exports.login_post = async function (req, res) {
User.findOne({ email: req.body.email }, function (err, user) {
if (user == null) {
return res.status(400).send({
message: "User not found"
})
}
else {
if (user.validPassword(req.body.password)) {
req.session._id = user._id.toString()
req.session.save(function (saveErr) {
req.session.reload(function (reloadSave) {
console.log(req.session, saveErr, reloadSave)
})
})
}
}
})
}
Here my whole function to get it from session
module.exports.session_get = function(req, res) {
return res.status(200).send(req.session);
}
module.exports.session_destroy = function(req, res) {
req.session.destroy();
return res.status(200).send({
message: "Session detroyed"
});
}
I have build a couple other expressjs applications, but I just can't find how to pass the User Model to the front-end or put it in the req as a parameter.
The app is a one page web app, so the user uses the login form to post to /login:
app.post('/login', require('./app/controllers/user').login);
the controller picks it up and calls the specific module in order to handle the request:
exports.login = function (req,res) {
AccountHandler.login(req,function (response) {
if (response.code < 0) {
req.flash('error', response.msg);
}
else if (response.code > 0) {
req.flash('success', response.msg);
}
req.user = response.user;
res.redirect(response.url);
});
}
and here is the module handling the login and calling the callback by passing the required arguments:
exports.login = function (req,callback) {
process.nextTick(function () {
User.findOne({ 'valid.email': req.body.Email }, function (err, user) {
if (err) {
callback({url: '/#login', msg: "There was an unexpected error!", code: -10});
}
if (!user) {
callback({url: '/#login', msg: "No such email/password combination was found", code: -1});
}
if (user) {
easyPbkdf2.verify(user.valid.salt, user.valid.password, req.body.Password, function (err, valid) {
if (!valid){
callback({url: '/#login', msg: "No such email/password combination was found", code: -1});
}
else{
callback({user: user, url: '/', msg: "acknowledged", code: 10});
}
});
}
});
});
}
In the Controller I am saying req.user = response.user; which doesn't persist, and when the user is redirected to '/' the req.user is still empty. how can I keep this user information to the redirect page?
If my understanding is correct, the res.redirect() call will actually cause the
browser to redirect to the given url, which will result in a new request to
your express server. Since its a new request, the old req object is no longer
relevant.
I think what you want is to store the logged in user's session using express session middleware. Lots of good examples are out there.. Here is one.
So the solution is to use session + middleware logic/functionality.
Here is the setup (exrepssjs):
Using the express-session module:
var sessionMiddleware = session({
resave: true,
saveUninitialized: true,
httpOnly: true,
genid: function(req) {
return uuid.v1() // I used another module to create a uuid
},
secret: 'random secret here',
cookieName: 'session', // if you dont name it here it defaults to connectsid
});
following create a middleware, so that you process the session on every request as follows (simplified, i do a lookup on the user overtime and pass it back to the front end):
app.use(function(req, res, next) {
res.locals.success_messages = req.flash('success_messages');
res.locals.error_messages = req.flash('error_messages');
if (req.session && req.session.user) {
User.findOne({ email: req.session.user.email }, function(err, user) {
if (user) {
req.user = user;
delete req.user.password; // delete the password from the session
req.session.user = user; //refresh the session value
res.locals.user = user;
}
// finishing processing the middleware and run the route
next();
});
} else {
next();
}
});
Now from the front-end most of the time using either ejs or jade you can access the user using the "user" variable.