Unable to proceed after setting cookie in a middleware - node.js

I'm unable to proceed the request after setting cookie in a middleware
const somemiddleware = (req, res, next) => {
// do stuff
req.cookie('accessToken', token, { signed: true, httpOnly: true, etc... });
console.log('it should run this line after setting cookie right?');
next();
}
router.post('/blah', somemiddleware, async (req, res) => {
// I expect req.signedCookies to be the token assigned from above
});
The terminal shows that the req ends after req.cookie and it does not proceed to the console log and rest of the code.

It's res.cookie(), not req.cookie().
You read cookies from the request. You set cookies on the response.
If you put a try/catch around it, you should see an exception and I'm surprised there wasn't anything about that in your server logs.

Related

Cookies do not stored when set it with axios POST method

I'm trying to write and read cookies and falling into a problem below.
This is my basic server side:
server.js
const app = express();
app.use(cors());
app.use(cookieParser());
import routes from '...';
app.use("/foo", routes);
app.listen(8888);
routes.js
const routes = express.Router();
routes.post('/', (req, res) => {
res.cookie("myFoo", "abcd");
res.send("Cookie added");
}
});
routes.get('/', (req, res) => {
res.send(req.cookies.myFoo);
}
});
export default routes;
And my client side at "http://localhost:3000".
I do two HTTP request
POST http://localhost:8888/foo
GET http://localhost:8888/foo
And get the response exactly what I expected abcd. Also, the cookie exists in the browser tab Application > Cookies too.
The problem cases when axios is used in the client.
const api = axios.create({
baseURL: "http://localhost:8888/foo"
});
async function setCookie(object) {
return api.post("/", object)
.then((res) => {
return res;
});
}
function getCookie() {
return api.get("/")
.then((res) => {
return res;
});
}
setCookie({})
.then((res) => {
getCookie();
})
The api.post() run usually and the header response Set-Cookie is correct. But cookies in the browser tab Application > Cookies are empty. Also, api.get() get the undefined.
I did try to move res.cookie() or the set cookie job in server side to GET route it WORKS on both HTTP and axios
routes.get('/', (req, res) => {
res.cookie("myFoo", "abcd");
});
tldr: Set cookie in HTTP POST method work fine but when client use axios to call so it causes problems.
Can you show me why this happened? And which code part went wrong that caused me into this?
Cookies are only used in cross-origin Ajax requests when:
The client asks to use them
The server grants permission to use them cross origin
So you need to change the client side code to ask for them:
const api = axios.create({
baseURL: 'http://localhost:8888/',
withCredentials: true,
});
And the server code to grant permission (note that you can't use credentials at the same time as the wildcard for origins).
app.use(cors({
origin: 'http://localhost:3000',
credentials: true
}));

Node Express CSRF token in not invalidated after logout

I'm using the standard express CSRF module. It seems to work correctly, except that after logging out and logging in again, it's still possible to use a CSRF token generated in the previous session. I thought it would be enough to invalidate the session - see example of code here, but that's not working:
const csrf = require("csurf");
const csrfProtection = csrf({ cookie: true });
//CSRF protected route
app.post("/editUser",csrfProtection,async function (req, res, next) {
//Do stuff
});
//Logout route
app.get("/logout", function (req, res) {
req.session.destroy();
});
What do I need to do to make sure CSRF tokens are invalidated on the user logging out?
Not sure if this is the best solution, but using the clearCookie function on both log out and log in routes seems to do the trick, e.g.
app.get("/logout", function (req, res) {
res.clearCookie("_csrf");
req.session.destroy();
});

Why request user is undefined in Sapper App

I cannot set a req.user in my Sapper app, what am I doing wrong
express()
.use(
bodyParser.urlencoded({
extended: true
}),
bodyParser.json(),
compression({
threshold: 0
}),
sirv('static', {
dev
}),
cookieParser(),
session({
store: store,
secret: 'somesecret',
resave: false,
saveUninitialized: true,
cookie: {
secure: true,
maxAge: 604800000,
}
}),
passport.initialize(),
passport.session(),
sapper.middleware({
session: (req, res) => {
let user = req.session.user
return {
user
}
}
}),
)
i have a login component from which im using fetching
in login folder i have a login.js file a server side route and i cannot get my req.user not req.session.user , not req.session.passport
I had the same problem.
I installed the module "express-session". (This module works with polka.js too)
And set the sapper's middleware:
sapper.middleware({
session: (req, res) => ({
user: req.session.user
})
I wrote a 'api/login/' to the sever side and a login page.
Once the server recognize the user with his login and pass', I use a goto(".").
The goto()'s behavior should be like a location.href. But this is not.
So I replace goto(".") by location.href("."). With this, I have the session's data from the server.(in the preload and with the stores => $session.user).
I hope that can help you.
I have found partial solution, please correct this if Iam wrong. So entire app is controlled over sapper.middleware and server it self has no automatic effect on it.
So no req.user is set, no session and no cookie value is set, every thing must be manually set from authentication route.
So my solution was to set cookie in login post route, so in login.js file.
import passport from 'passport'
export async function post(req, res, next) {
passport.authenticate('local', (err, user, info) => {
if (err) {
return res.json(err)
}
if (user) {
// console.log(req)
return res.status(201).cookie('user', req.sessionID).json({
email: user.email,
id: user._id
})
}
if (info) {
return res.json(info)
}
})(req, res, next);
}
in login.svelte component i set my session (SAPPER MIDDLEWARE SESSION)
session.set({ user: true, email: user_data.email, id: user_data.id });
So my Further action is to check if sessionid exist in session store on server if yes continue if no set user to null and redirect to login.
is it correct ? Is it safe enough ?
Edit
So My partial "solution" is not right for any one having the same problem with passport authentication solution is
you must define route in server.js prior sapper.middleware
app.httpRequest('/route',YOURAUTHENTICATION MIDDLEWARE)
in your route.js file or in route folder index.js file you will have your req.user
in sapper middleware your session object must look like this
session: (req, res) => ({
user: req.session.passport ? req.session.passport.user : null })
if you will try to pass req.user it will not be serialized by sapper / devalue package and you will keep getting error.
After this in your route.js you can preload user from session. hope this was clear !

Passport callback not being called

While trying to implement the passport-same authentication methods I have hit a roadblock. The callback function passed to the passport.authenticated function does not get called.
router.post("/saml/callback",
function (req, res, next) {
req.body.SAMLResponse = req.body.SAMLResponse.replace(/[\n\r]/g, "");
next();
},
function (req, res, next) {
console.log("Calling passport handler");
console.log(req.body);
try {
const response = passport.authenticate("saml",
{
failureRedirect: "/saml/error",
failureFlash: true
}, (error, user, info) => {
console.log(error, user, info);
next();
})(req, res, next);
console.log(response);
} catch(e) {
console.log(e);
}
console.log("Line after passport handler");
},
function (req, res) {
res.redirect("/saml/success");
}
);
My express app hangs when entering this method but only with 1 specific saml provider (using https://samltest.id as test provider DOES work with the exact same code). It seems that an error occurs in this authenticate method but I cannot for the live of me find this error.
How do I get the error in this callback.
Log output:
Calling passport handler
{SAMLResponse: 'base64encoded saml response'}
undefined
Line after passport handler
Error: connect ETIMEDOUT ip:443
The problem turned out to not at all be in this piece of code but instead in the initiation of the saml provider.
I have a callback function in Strategy({cert: function(callback)....}); which tries to fetch the signing certificates for the saml response. The IDP was however not accessible from my SP due to it being a test server, therefor the callback never got called.
TL;DR; If you are using the cert key in your Strategy definition; and said key utilises a callback, check if that callback gets called!

How to log out from basicAuth (Express)

I'm trying to set up a web server using express. To access this server, users have to authenticate and for that, I use the basicAuth() middleware provided by Express. It works perfectly, except that I do not know how to log out once I logged in ! I have to close my browser to disconnect, but instead I would like to have a "disconnect" page which would redirect towards the "login" page (this one with the hideous form used to log in...).
Does anyone has an idea ?
Thanks per advance
PS : I apologize for my pathetic English :)
Express' basicAuth uses HTTP Basic Authentication whose implementation doesn't need HTML pages, cookies nor session ids. Its main drawbacks are its not secure and, our concern here, there is no mechanism in the spec for the server to instruct the browser to log out.
express.basicAuth() calls require(blah-blah/connect/lib/utils).unauthorized() which sends a 401 status with header 'WWW-Authenticate: Basic realm="..."'. The browser handles the authentication window and from then on sends a header like 'Authorization: Basic YmFzaWM6YmFzaWM=' which contains the username and password.
(express.basicAuth is not secure, especially over HTTP, because you can get the username:password with
new Buffer('YmFzaWM6YmFzaWM=' , 'base64').toString()
which gives basic:basic in this case.)
Our problem is the HTTP spec does not provide a way to stop that header being sent. A workaround for HTTPS is to redirect the user to a URL on the same domain having incorrect credentials.
The HTTP workaround I use for Express V3 can be used with app.use(express.basicAuth(...)). I find this easier to use than other solutions which require a call to middleware in every secured route, e.g. app.get('/secure', checkAuth, function (req, res) {...}).
Here is a working example.
var express = require('express'),
http = require('http'),
app = express();
app.use(express.favicon()); // prevent interference during test
app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(express.session({ secret: 'winter is coming' }));
app.use(function (req, res, next) {
if (!req.session.authStatus || 'loggedOut' === req.session.authStatus) {
req.session.authStatus = 'loggingIn';
// cause Express to issue 401 status so browser asks for authentication
req.user = false;
req.remoteUser = false;
if (req.headers && req.headers.authorization) { delete req.headers.authorization; }
}
next();
});
app.use(express.basicAuth(function(user, pass, callback) {
callback(null, user === 'basic' && pass === 'basic');
}, '***** Enter user= basic & password= basic'));
app.use(function (req, res, next) {
req.session.authStatus = 'loggedIn';
next();
});
app.use(app.router);
app.get('/secure', function (req, res) {
res.send([
'You are on a secured page.',
'<br>',
'Refresh this page without having to log in again.',
'<br/>',
'Log out.'
].join(''));
});
app.get('/logout', function (req, res) {
delete req.session.authStatus;
res.send([
'You are now logged out.',
'<br/>',
'Return to the secure page. You will have to log in again.',
].join(''));
});
http.createServer(app).listen(3000, function(){
console.log('Express server listening on port 3000. Point browser to route /secure');
});
P.S. Your English is excellent.
For express.js 4 and basicAuth, you can use this method:
app.get('/logout', function (req, res) {
res.set('WWW-Authenticate', 'Basic realm=Authorization Required');
return res.sendStatus(401);
});
Adding to wyllman, the 401 code is Unauthorized.
You can simply respond with res.status(401).send('Logged out')
or
app.get('/logout', function (req, res) {
res.status(401).send('Logged out')
//or res.status(401).end() if you don't want to send any message
});
I've confirmed that redirecting the user to a /logout page with an http code 401 and html with <a> element links to /login works.

Resources