How to use variables in jade file after it's rendered - node.js

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.

Related

User sessions data does not persist when calling from a different route

My user session does not persist within the server. I can see within the log that I saved it in my /login route, but when I try to access it from a different route, its "undefined".
My /login route:
app.route("/login")
.post(async (req, res) => {
var username = req.body.username,
password = req.body.password;
console.log('\t we are here')
try {
var user = await User.findOne({ username: username }).exec();
if(!user) {
res.redirect("/login");
}
user.comparePassword(password, (error, match) => {
if(!match) {
console.log('Password Mismatch');
console.log('Ensure redirect to /login');
res.redirect("/login");
}
});
req.session.user = user;
console.log('\t\treq.session:');
console.log(req.session)
var redir = { redirect: "/dashboard" };
return res.json(redir);
} catch (error) {
console.log(error)
}
});
In the above snippet I try to save the session data by req.session.user = user;. Its log appears as:
But now when I try to call the session I just stored, it shows "undefined". This is my /dashboard route & its corresponding log:
app.get("/dashboard", (req, res) => {
console.log(req.session.user_sid);
// console.log(req.cookies.user_sid);
if (req.session.user && req.cookies.user_sid) {
// res.sendFile(__dirname + "/public/dashboard.html");
console.log(req.session);
res.send("send something")
} else {
res.send("go back to /login");
}
});
To my understanding, user authentication is done my checking sessions and cookies, which is why I'm trying to save the session to request.session. I want to the data to persist so that I can use it in all my other routes such as when calling /dashboard api.
Dashboard api will be call by a protected route like when the user is logged in.

Cannot save User in session data

I am creating a MERN-dashboard, with a login, registration & a dashboard where only logged in Users have access to. Now I've managed to get the user registration and login working, however I seem to be missing something when it comes to saving the User in the express-session.
This is what I use for the login
app.post('/login', async (req, res) => {
const username = req.body.username;
const password = req.body.password;
const newUser = await User.findOne( {
username: username,
})
if (newUser) {
bcrypt.compare(password, newUser.get("passwd"), (error, result) => {
if (result) {
console.log(newUser)
req.session.user = newUser
req.session.loggedIn = true
res.send({newUser, message: 'Successful Login!'})
} else {
res.send({message: 'Wrong password!'})
}
})
} else {
res.send({message: 'User not found.'})
}
})
And this is how my frontend is checking if a User is logged in
app.get('/login', (req, res) => {
if (req.session.user) {
res.send({loggedIn: true, user: req.session.user})
} else {
res.send({loggedIn: false})
}
})
Now, if I log myself in the POST request signals me, that all is fine. However if I reload the page, the GET request tells me I am not logged in.
I have tried reading several articles about express-session but did not manage to find the solution to my problem.
Thank you in advance.

api call getting affected by another api call's validation

not really sure if my title is correct but my problem is that I have this reset password token checker in my api that seems to get affected by another api that finds a specific user, this api has user validation.
Here is what they look like:
//get specific user
router.get('/:id', validateToken, async (req, res) => {
const id = req.params.id
const user = await User.findByPk(id);
res.json(user);
});
//reset-password token check
router.get('/reset-pass', async (req, res) => {
await User.findOne({
where: {
resetPasswordToken: req.body.resetPasswordToken,
resetPasswordExpires: {
[Op.gt]: Date.now()
}
}
}).then(user => {
if(!user) {
res.status(401).json({ error: 'Password reset link is invalid or has expired.'})
} else {
res.status(200).send({
username: user.username,
message: 'Password reset link Ok!'
});
}
});
});
then here is the validateToken
const validateToken = (req, res, next) => {
const accessToken = req.cookies['access-token'];
if (!accessToken)
return res.status(401).json({error: 'User not authenticated!'});
try {
const validToken = verify(accessToken, JWT_SECRET)
req.user = validToken;
if(validToken) {
req.authenticated = true;
return next();
}
} catch(err) {
res.clearCookie('access-token')
return res.status(400).json({error: err}).redirect('/');
}
};
when I comment out the get specific user api the reset password token check works. If I remove validateToken it returns null instead of giving me the username and message.
One of the things I notice is the route param "/:id", that means that literally everything would be processed by get specific user because all routes start with "/", only use params in routes with a prefix like "/user/:id" that way only the routes that starts with "/user" will execute that code.
Change your code to:
//get specific user
router.get('/user/:id', validateToken, async (req, res) => {
const id = req.params.id
const user = await User.findByPk(id);
res.json(user);
});

Authentication with bcrypt and jwt does not work

I am quite new to Node.js / Express and development of web apps. I try to do a simple user registration where I hash the password with bcrypt before saving the hash to mongodb. The login form, which should allow a user to login, does subsequently lookup a user in the db and then compares the two passwords.
Certain routes in my web app I do want to protect so that only authenticated user have access to them. So when successfully login in I do send a Json Web Token (jwt) along the response header which should then be used - when redirected to the protected '/lobby' route - to authenticate the user and allow him / her to proceed to that route.
However, I always get the following error:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
So it looks like it already sends back a response to the client before trying to set the header which of course is then not possible anymore.
I would highly appreciate your help here!
I do use the following code:
Register function
async function register(req, res) {
//Check with user already exists
const emailExists = await User.findOne({email: req.body.email});
if(emailExists) {
return res.status(400).send('User already exists!');
};
//Hash the password and create new user from request data
bcrypt.hash(req.body.password, 10, async function (err, hashedPass){
if(err){
res.json({
error: err
});
}
let user = new User({
name: req.body.name,
email: req.body.email,
username: req.body.username,
password: hashedPass,
password2: hashedPass
});
try {
await user.save();
}catch (err) {
res.status(400).send(err);
};
});
res.render('index');
};
Login function
async function login(req, res) {
const user = await User.findOne({email: req.body.email});
if(!user) {
return res.status(400).json({message: 'User not found!'}).render('index');
};
bcrypt.compare(req.body.password, user.password).then((result)=> {
if(result){
const token = jwt.sign({_id: user._id}, process.env.TOKEN_SECRET);
res.setHeader('auth-token', token.toString());
res.redirect('/lobby');
}else {
return res.status(400).json({message: 'Passwords do not match!'}).render('index');
}
}).catch((err)=> {
console.log(err);
});
};
As a middleware to the '/lobby' route (i.e. when someone does a get request to '/lobby') I use a "verifyToken" function which should ensure correct authentication of the user via jwt.
verifyToken function
const jwt = require('jsonwebtoken');
module.exports = function(req, res, next) {
console.log('verify function started');
const token = req.header('auth-token');
console.log(token);
if(!token) {
res.status(401).json({
message: 'Access denied!'
});
};
try {
const verified = jwt.verify(token, process.env.TOKEN_SECRET);
req.user = verified;
next();
}catch (err) {
res.status(400).json({
message: 'Invalid token!'
});
};
};
As said, I would very much appreciate your help here! I assume the problem is much simpler than I think it is :-).
Cheers
You forgot to return the response in few cases. So it continues to execute other code aswell, that's where server trying to send the response again, which is why you're getting that error.
Change your response like the following.
verifyToken function
const jwt = require('jsonwebtoken');
module.exports = function(req, res, next) {
console.log('verify function started');
const token = req.header('auth-token');
console.log(token);
if(!token) {
return res.status(401).json({ // <-- here you need to `return`
message: 'Access denied!'
});
};
try {
const verified = jwt.verify(token, process.env.TOKEN_SECRET);
req.user = verified;
next();
}catch (err) {
return res.status(400).json({
message: 'Invalid token!'
});
};
};
Register function
async function register(req, res) {
//Check with user already exists
const emailExists = await User.findOne({email: req.body.email});
if(emailExists) {
return res.status(400).send('User already exists!');
};
//Hash the password and create new user from request data
bcrypt.hash(req.body.password, 10, async function (err, hashedPass){
if(err) {
return res.json({ // <-- here as well
error: err
});
}
let user = new User({
name: req.body.name,
email: req.body.email,
username: req.body.username,
password: hashedPass,
password2: hashedPass
});
try {
await user.save();
return res.render('index'); // <-- assuming this is your success response
}catch (err) {
return res.status(400).send(err); <-- here too
};
});
};
Looks like in the Login function the header gets set. I can see this via console.log(res.header('auth-token'));. Subsequently the redirect to "/lobby" gets called because the verifyToken function does start.
However, in the verifyToken function the respective header is then undefined. Because I always also get a 'Access denied!' message.
As said, I do call the verifyToken function as middleware when doing a get request to the /lobby route. The route for '/lobby' looks as follows:
const express = require('express');
const router = express.Router();
const lobbyCtrl = require('../controllers/lobby');
const verify = require('./verifyToken');
router.get('/', verify, lobbyCtrl.display);
module.exports = router;

Issue with setting Authorization header using Express JS implemenitng Using Cognito User Pool Authentication

I am implementing authentication using Cognito User Pool. I am only using NodeJS + Express JS, so whole coding is on server side and client side I just render html file using templates.
I am using below code to autheticate the user passing username and password.
const cognitoProvider = new AWS.CognitoIdentityServiceProvider();
cognitoProvider.adminInitiateAuth(params, function (err, result) {})
If authentication is sucessfull, result object has the accesstoken, tokentype and refresh token. Now I am not clear on how to set Authorzation header with this result which has the token. After this code I redirect user to the home page where I welcome the user. In subsequent request I want to retrieve token from header to check if user has logged in. But issue is I am not able to set the authorization header.
Below is the code:
cognitoProvider.adminInitiateAuth(params, function (err, result) {
If (err == null)
{
res.set('Authorization', result.AuthenticationResult); // Is this correct ? Even If i use this in next request I cannot retrieve Authorization header using req.header['Authorization']. I am not sure if I am coding this correctly.
//Below is my next line of code
res.redirect('/home'); // I redirect user to home page. I am not using render here as I was to avoid the post back message in case user tries to refresh the page, so I use redirect.
}
})
I have spend hours but not able to make this work. May be I am missing some basic concept here. Code Below of /users where I pass username & Password:
var express = require('express');
var router = express.Router();
const AWS = require('aws-sdk');
AWS.config.region = "REGION COMES HERE";
const cognitoProvider = new AWS.CognitoIdentityServiceProvider();
router.post('/', function (req, res, next) {
const username = req.body['InputEmail'];
const password = req.body['InputPassword'];
const params = {
"AuthFlow": "ADMIN_USER_PASSWORD_AUTH",
"AuthParameters": {
"USERNAME": username,
"PASSWORD": password
},
"UserPoolId": "USERPOOLID",
"ClientId": "CLIENTID"
}
cognitoProvider.adminInitiateAuth(params, function (err, result) {
if (err) {
var errormessage;
switch (err.code) {
case 'NotAuthorizedException':
errormessage = err.message
break;
default:
errormessage = "Issues encountered. Try Again";
}
res.render('index', { title: 'Express', message: errormessage });
return;
}
if (result.ChallengeName) {
switch (result.ChallengeName) {
case 'NEW_PASSWORD_REQUIRED':
res.render('firsttimelogin', { title: 'Express', username:username, message: '' });
break;
default:
res.render('index', { title: 'Express', message: '' });
}
} else {
res.set('Authorization', result.AuthenticationResult);
res.redirect('/home');
}
});
});
module.exports = router;
Code of /Home
var express = require('express');
var router = express.Router();
router.use('/', function(req, res, next) {
var token = req.header['Authorization'];
if (token) {
res.render('home', { title: 'Express'});
}
else{
res.redirect('/');
}
});
module.exports = router;

Resources