I have req.session variables that I am setting upon login, like so:
req.session.loggedin = true
req.session.firstname = loginDetails.firstName;
what I want to do is pass this information to ALL routes (I have nearly 60, and don't want to go through all of them and add these in manually), and also every route I have calls the front-end using this:
res.render('page.ejs', {data: rows}), so I would ideally want it to pass it to the front-end pages so I can access them there too. Not sure if this is possible, but worth a shot! thx 4 the help in advance!
You can create a middleware function and add variables to an existing express-session.
app.js
const express = require("express");
const session = require("express-session");
// express app
const app = express();
app.use(express.json());
// init example session with express session
app.use(session({ resave: true, secret: "123456", saveUninitialized: true }));
// use a middleware function
app.use((req, res, next) => {
if (!req.session.initialised) {
// init variables you want to set in req.session
req.session.loggedin = true;
req.session.firstname = "john doe";
}
next();
});
// test api endpoint
app.use("/testroute", require("./routes/api/test"));
// run server
const PORT = process.env.PORT || 8000;
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
Then you can query your variables like req.session.firstname in any other route in the request object. You can also update it from your routes
test.js
// testroute
const router = express.Router();
router.get("/", async (req, res) => {
// get variables from request object
try {
console.log(req.session.firstname);
console.log(req.session.loggedin);
// this would update the session variable if uncommented
// req.session.firstname = "dagobert"
// console.log(req.session.firstname);
res.status(200).json({ message: `Your firstname is ${req.session.firstname}` });
} catch (error) {
res.status(400).json({ message: error.message });
}
});
module.exports = router;
Related
I set a cookie with,
router.get("/addCartToCookie", function(req, res) {
let options = {
maxAge: 1000 * 60 * 15,
httpOnly: true,
};
let cartData = {
name: "test cookie",
slug: slugify("test cookie"),
productPictures: "K6B4dzGjN-teal.jpeg",
price: 200,
description: "testing cookies",
quantity: 7,
};
// Set cookie
res.cookie("cartName", cartData, options);
res.send("Cart Added to Cookie");
});
Which perfectly sets a cookie named "cartName". But once I try to get the cookie,
It shows "cartName" is undefined.
Get Cookie Code:
router.get("/getCartFromCookie", function (req, res) {
res.send(req.cookies["cartName"]);
console.log(req.cookies);
});
I tried console logging the cookies but it also shows undefined.
From what I know, the optimized way of handling cookies is by using cookie-parser dependency for express.
$ npm install cookie-parser
Then you could easily fetch your cookies using req.cookies or req.signedCookies property like below:
var express = require('express')
var cookieParser = require('cookie-parser')
var app = express()
app.use(cookieParser())
app.get('/', function (req, res) {
// Cookies that have not been signed
console.log('Cookies: ', req.cookies)
// Cookies that have been signed
console.log('Signed Cookies: ', req.signedCookies)
// Your cart cookie
console.log('Cookies: ', req.cookies.cartName)
})
app.listen(8080);
References:
https://github.com/expressjs/cookie-parser
http://expressjs.com/en/resources/middleware/cookie-parser.html
You don't show all your code, but I assume you're using the cookie-parser middleware to access the cookies directly on the request object. If not, start by adding cookie-parser and go from there.
For your cookie reading issue, be aware that you can't send and read cookies on the same request. You need one request to set the cookie and another to read the cookie.
import express from 'express';
import cookieParser from 'cookie-parser'
import {inspect} from 'util';
const app = express();
const router = express.Router()
router.use(cookieParser())
app.use(router);
const port = 3000
router.get('/setcookie', (req, res) =>{
res.cookie('testCookie', 'magic content');
res.send("set the cookie");
})
router.get('/readCookie', (req, res) =>{
res.send('your cookies: ' + inspect(req.cookies['testCookie']));
console.log(req.cookies);
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
I have problems with setting up csrf. I hope that someone can point me in the right direction.
I'm using next.js with express.js.
When I refresh the page following happens:
I get a _csurf cookie (dev tools > application > cookies)
a csrf token is logged in my console (-> see last code snipped from context)
when I make a POST request (-> see routes/index.js f.ex. "/aignupQ"), I get the error "Invalid csurf token"; in the request header I can see the _csrf cookie
when I refresh the page and make the POST request again everything works.
I'm really confused by the error and really don't understand what is wrong. Here is some relevant code:
server.js:
require("dotenv").config();
const express = require("express");
const next = require("next");
const bodyParser = require("body-parser");
const cors = require("cors");
const cookieParser = require("cookie-parser");
const routes = require('./routes');
const csrf = require("csurf");
const csrfProtection = csrf({
cookie: true,
});
//next.js configuration
const dev = process.env.NODE_DEV !== "production";
const nextApp = next({ dev });
const port = 3000;
const handle = nextApp.getRequestHandler();
nextApp.prepare().then(() => {
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cors());
app.use(cookieParser());
app.use((err, req, res, next) => {
res.status(500).send("Something went wrong!");
});
app.use(csrfProtection);
app.use('/api', routes);
app.get("*", (req, res) => {
return handle(req, res);
});
//start server
app.listen(port, (err) => {
if (err) throw err;
console.log(`listening on port ${port}`);
});
});
routes/index.js:
const express = require('express')
const router = express.Router()
const getCsrfToken = require('../controllers/csrf')
const postSignupQ = require("../controllers/postSignupQ");
const attachUser = require("../middleware/attachUser");
router.get("/csrfToken", getCsrfToken.getCsrfToken);
router.use(attachUser);
router.post("/signupQ", postSignupQ.postSignupQ);
module.exports = router
controllers/csrf.js
const getCsrfToken = (req, res) => {
res.json({ csrfToken: req.csrfToken() });
};
module.exports = { getCsrfToken };
context - here I console.log(csrf):
useEffect(() => {
const getCsrfToken = async() => {
const { data } = await axios.get('api/csrfToken');
console.log("csurf", data);
axios.defaults.headers['X-CSRF-Token'] = data.csrfToken;
};
getCsrfToken();
}, []);
I don't understand why I get the error message, when I make a POST request for the first time and when I refresh the page everything works. What's the problem and how can I solve this?
EDIT
Thanks to Jack Yu the code snippets above are working. Maybe it can help someone else..
EDIT
I also found your api path might be wrong. In your axios is await axios.get('csrfToken'). But, I saw /api/csrfToken in your router. Change it to await axios.get('/api/csrfToken')
Original Answer
In csurf package, when you use csurf({cookie: true}) with cookie mode in middleware at multiple times, it'll break the csrf token in response header with first time post. You could take a look for more detail in CSRF doesn't work on the first post attempt, I've explain the reason in that post. So, there are two solutions you could use.
Solution 1
According to the comments, you use app.use(csruf({cookie: true})) in server.js and router/index.js. Remove the following line in your router/index.js. When you setup csruf in server.js, you could use req.csrfToken() in controllers/csrf.js without setting up csruf again.
const csrf = require("csurf");
const csrfProtection = csrf({
cookie: true,
});
router.use(csrfProtection);
Solution 2
You'll need to use express-session package. Add following code before the csurf. If you have .use(csrf({cookie: true})) in your routes/index.js, remove it.
const session = require('express-session')
// mark1: change it to false
const csrfProtection = csrf({
cookie: false,
});
// blablabla ... other code
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cors());
app.use(cookieParser());
// mark2: put here
app.use(session({
name: "test",
secret: "test",
cookie: { maxAge: 3 * 60 * 60 * 1000 },
resave: false,
saveUninitialized: false
}))
// put here
app.use((err, req, res, next) => {
res.status(500).send("Something went wrong!");
});
app.use(csrfProtection);
app.use('/api', routes);
Then change {cookie: true} to {cookie: false} in all csurf setting. When you use session mode, you could use csruf({cookie: false}) many times in middleware.
We need to pass a cookie in header like below:
headerMaps.put("cookie", "_csrf=value; connect.sid=value; csrfToken=value")
Background:
I have an express application with some simple routes and Router-level Middleware. I want to register an Application-Level Middleware.
The Problem
In Router-Level Middlewares. I can access req.route object. But I can not access the same object inside an Application-Level Middleware.
I can understand this, as inside Application-Level middlewares the program is not inside the route yet.
But is there any way to get req.route object or something equivalent to req.route.path inside global middlewares?
req.path or req.originalUrl contains the real url not the route path.
Example
const express = require('express');
const app = express();
const port = 3232;
app.use((req, res, next) => {
const route = req.route; // It is an application level middleware, route is null
return next();
});
app.get('/test/:someParams', (req, res) => {
const route = req.route; // can access req.route here because it is a Router-level middleware
console.log(route.path)
console.log(req.path)
return res.send('test')
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
Output
Request: GET#localhost:3232/test/33333
/test/33333 // I don't need this.
/test/:someParams // This is what I want to get inside the Application-Level Middleware
Alternative Solution
An alternative solution to this problem can be as the following
const express = require('express');
const app = express();
const port = 3232;
function globalMiddleware(req, res, next) {
const route = req.route;
console.log(route) // can access it
return next();
}
app.use((req, res, next) => {
const route = req.route; // It is an application level middleware, route is null
return next();
});
app.get('/test/:someParams', globalMiddleware, (req, res) => {
const route = req.route; // can access req.route here because it is a Router-level middleware
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
But injecting the same middleware to each and all of my routes does not sound like a smart solution. Specially on bigger applications.
A dump of router object
{
"path":"/test/:someParams",
"stack":[
{
"name":"globalMiddleware",
"keys":[
],
"regexp":{
"fast_star":false,
"fast_slash":false
},
"method":"get"
},
{
"name":"<anonymous>",
"keys":[
],
"regexp":{
"fast_star":false,
"fast_slash":false
},
"method":"get"
}
],
"methods":{
"get":true
}
}
the path key is the thing I want to get. Please note that req.route.path is not the same as req.path
I was having this difficulty too. I didn't find anything on the internet that would solve it so I tried to search the express code itself for what it did to find the route. Basically it is the same thing as the code below, it looks for which regexp is valid for that route. (Google Translate)
const url = req.originalUrl.split('?')[0] // Routes with query
const layer = req.app._router.stack.find(layer => {
return layer.regexp.exec(url) && layer.route
})
So you can access the original path:
console.log(layer.route.path)
What data do you expect in req.route?
you can use req.url, req.method, req.originalUrl, etc...
or in Application level middleware you can add new field to req object
req.customRoute = {yourField: "yourValue"}
and this field will available in route level middleware
You can use middleware as per request
const middleware = (req, res, next) => {
// Append what you want in req variable
req.route = "abc" // let abc
req.path = "path" // req.route.path
next();
}
You can get it here from middleware
app.get('/test/:someParams', middleware, (req, res) => {
console.log(req.params.someParams)
console.log(req.route) // "abc"
console.log(req.path) // "path"
});
For application level middleware
app.use((req, res, next) => {
req.route = "abc" // let abc
req.path = "path" // or req.route.path
next()
})
I have this simple enough express application which tries to use Active Directory to authenticate the user. Here is my setup:
const express = require('express');
const session = require('express-session');
const passport = require('passport');
const ActiveDirectoryStrategy = require('passport-activedirectory');
const PORT = process.env.PORT || 8080;
// AD configuration. Real values omitted.
const config = {
url: 'ldaps://...',
baseDN: '...',
username: '...',
password: '...'
};
const app = express();
app.use(session({
secret: 'mysessionsecret',
resave: true,
saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());
passport.use('ad', new ActiveDirectoryStrategy(
{
ldap: config
},
(profile, ad, done) => {
// The problem is here! This never gets called!
console.log('ActiveDirectoryStrategy activated.');
done('ActiveDirectoryStrategy not implemented.');
}
));
// route middleware to ensure user is logged in
const isLoggedIn = (req, res, next) =>
req.isAuthenticated() ? next() : res.redirect('/auth');
app.get('/', isLoggedIn, (req, res) => {
res.end('You are logged in.');
});
app.get('/unsecured', (req, res) => {
res.end('You are not logged in.');
});
app.get('/auth', passport.authenticate('ad', {
successRedirect: '/',
failureRedirect: '/unsecured'
}));
app.listen(PORT);
console.log('Listening on port ' + PORT);
However, the verifier function I pass for the constructor ActiveDirectoryStrategy never gets called. (This is the function with the signature (profile, ad, done)).
I am sure that there is no problem with the LDAP configuration, because I can access the active directory just fine with activedirectory module with the same parameters:
const ActiveDirectory = require('activedirectory');
const ad = new ActiveDirectory(config);
ad.findUser('username', (err, user) => {
if (err) {
return console.log(err);
}
console.log(JSON.stringify(user));
// prints an object with user's info
});
So there must be a problem with my routing. What am I doing wrong? Why is my verifier function not getting called?
The problem was a misconception I had. I thought the Passport.js's authenticate middleware would perform the NTLM handshake for me. This is not the case. passport-activedirectory actually needs something like IISNode to run in front of it in order to perform the NTLM handshake. Since the request does not contain the authentication information,
I settled on using express-ntlm middleware as a result. express-ntlm gives you the UserName, DomainName, and Workstation properties you can use. But if you want to acquire the full AD profile for some reason, you could setup a custom passport strategy like so:
const ActiveDirectory = require('activedirectory');
const CustomStrategy = require('passport-custom');
passport.use('ntlm-ad-backend', new CustomStrategy((req, done) => {
let username = req.ntlm.UserName;
AD.findUser(username, (err, profile) => {
if (err) {
console.log(err);
done(err);
}
if (!profile) {
done(new Error(`User ${req.ntlm.UserName} not found in Active Director.`));
} else {
done(null, profile);
}
});
}));
And then,
app.get('/auth', passport.authenticate('ntlm-ad-backend', {
successRedirect: '/',
failureRedirect: '/unsecured'
}));
Note that you will also have to implement serializeUser and deserializeUser.
I have a simple example that I am following.
For some reason the request always times out, wondering if you have any ideas why this could be happening.
// server.js
// BASE SETUP
// =============================================================================
// call the packages we need
var express = require('express'); // call express
var app = express(); // define our app using express
var bodyParser = require('body-parser');
// configure app to use bodyParser()
// this will let us get the data from a POST
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
var port = process.env.PORT || 8080; // set our port
//load the model
var Bear = require('./models/bear');
// ROUTES FOR OUR API
// =============================================================================
var router = express.Router(); // get an instance of the express Router
// middleware to use for all requests
router.use(function(req, res, next) {
// do logging
console.log('Something is happening.');
next(); // make sure we go to the next routes and don't stop here
});
router.route('/bears')
// create a bear (accessed at POST http://localhost:8080/api/bears)
.post(function(req, res) {
var bear = new Bear(); // create a new instance of the Bear model
bear.name = req.body.name; // set the bears name (comes from the request)
// save the bear and check for errors
bear.save(function(err) {
if (err)
res.send(err);
//res.json({ message: 'Bear created!' });
res.json({ message: 'hooray! welcome to our api!' });
});
});
// test route to make sure everything is working (accessed at GET http://localhost:8080/api)
router.get('/', function(req, res) {
res.json({ message: 'hooray! welcome to our api!' });
});
// more routes for our API will happen here
// REGISTER OUR ROUTES -------------------------------
// all of our routes will be prefixed with /api
app.use('/api', router);
// START THE SERVER
// =============================================================================
app.listen(port);
console.log('Magic happens on port ' + port);
Let me know what you think. Essentially I can do GETS no problem, but when I get to /bears and do a POST x-www-form-urlencoded with a param name 'Klaus' it simply hangs. I can't understand why.
Any suggestions much appreciated.
Many thanks for your help.