Nodejs server doesn't recognize saved cookie sessionId - node.js

I have a nodejs/express server with the following code
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const cookieparser = require("cookie-parser");
const { randomBytes } = require('crypto');
const COOKIE_SECRET = 'aavslrhe158ewuycvasjy7et2hvh2ytt0';
var SESSIONS = {};
app.use(
express.static(__dirname + '/public'),
bodyParser.urlencoded({ extended: false }),
bodyParser.json(),
cookieparser(COOKIE_SECRET)
);
app.get("/login", function (request, response){
response.sendFile(__dirname + "/views/login.html");
});
app.post("/verifyaccount", function (request, response){
const nextSessionId = randomBytes(16).toString('base64');
response.cookie("sessionId", nextSessionId, { maxAge: 3600, httpOnly: true, Secure: true });
SESSIONS[nextSessionId] = request.body.sz_Username;
response.status(response_status).redirect('/admin');
}
app.get("/admin", function (request, response){
if(!is_authorized(request.cookies.sessionId)){
response.redirect('/login');
}
else{
response.sendFile(__dirname + "/views/admin.html");
}
});
app.post("/addproject", function(request, response){
if(!is_authorized(request.cookies.sessionId)){
response.redirect('/login');
}
else{
}
}
function is_authorized(sessionId){
var authorized = false;
if (SESSIONS[sessionId]) {
authorized = true;
}
return authorized;
}
So when I login the credentials go to /verifyaccount, there I check if they're correct. Then it creates a cookie in my browser: sessionId:"KlS6xuspQ4GczVqqpSc2Nw%3D%3D" and stores it in the SESSIONS variable. I get redirect to /admin where the authorization works.
But when I am in admin.html and send data to /addproject I get redirect to /login because the authorization fails. The request.cookies.sessionId is undefined. The cookie keeps existing in my browser, so I don't know what the problem is, since the cookie was correct in /admin.
Edit: after being redirect to /admin from /login if I go back to /login or / and then attempt to /admin from the url I get the same undefined error. Which should not occur since the cookie expires in 1 hour.

"maxAge is in milliseconds. Your cookie is expiring 3.6 seconds after you set it"
-clubby789 htb

Related

Setting single cookie with multiple values

I am trying to set a single cookie key-value pair with multiple values in nodeJS. The reason for the single cookie is that I'm sending Token and Secret variables that are linked as part of authentication, separating these out over two cookies may cause issues. I followed a tutorial that suggests stingify method.
When tested in Postman, the cookie seems to be encoded with token and secret. i.e. '%7B%22' etc. Is this as expected, if yes how do I parse values when the cookie is sent to server.
// userToken and mySecret test values.
CustomerRoute.post('/login', (req, res) => {
...
...
var mycookie = JSON.stringify({userToken:1234,mySecret:5678});
res.cookie('ID', mycookie, {HttpOnly:true, maxAge:20*60*1000, sameSite: 'strict'});
res.apiSuccess(resInfo);
Cookie in postman:
ID=%7B%22userToken%22%3A1234%2C%22mySecret%22%3A5678%7D; Path=/; Domain=localhost; Expires=Tue, 02 Mar 2021 17:37:24 GMT;
UPDATE -
I also managed to send the two tokens without stingify by simply concatenating the two strings.
var mycookie = 'Token='+'1234'+'Secret='+'5678';
UPDATE2
I'm using cookie-parser. When I call route:
.post('/data1', (req, res) => {
//var rc = req.headers.cookie;
const { cookies } = req;
console.log(cookies);
res.apiSuccess();
In console I get:
{ ID: '{"userToken":1234,"mySecret":5678}' }
Whats the best method to split values to variables?
UPDATE3 - As a recap. I want to write a single cookie with userToken and mySecret, then in the /data route verify (this will eventually form a function in middleware)
const http = require('http');
const https = require('https');
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
var cookieParser = require('cookie-parser')
const config = require('./config');
var corsOptions = {
origin: 'http://example.com',
optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
}
/* init server */
const server = express();
/* middleware */
server.use(express.json());
server.use(cookieParser());
server.use(express.static('public'))
server.use(bodyParser.json({
limit: "10000kb"
}));
server.use(bodyParser.urlencoded({
extended: true,
limit: "10000kb"
}));
server.use(function (req, res, next) {
res.apiError = function (message) {
res.json({
status: false,
message: message
})
};
res.apiSuccess = function (data) {
res.json({
status: true,
data: data
})
};
next();
})
Create cookie - login function
var mycookie = JSON.stringify({userToken:1234,mySecret:5678});
res.cookie('session_id', mycookie, {HttpOnly:true, maxAge:20*60*1000, sameSite: 'strict'});
Read cookie:
CustomerRoute.post('/data1', (req, res) => {
// var rc = req.headers.cookie;
const { cookies } = req;
console.log(cookies);
if ('session_id' in cookies) {
console.log('Session Id exists');
var points = JSON.parse(cookies);
//console.log(cookies['id']);
console.log(points);
}
res.apiSuccess();

Reading Cookies from Express Middleware

I'm making an app using the Express and Unblocker modules, along with CookieParser for cookie reading. I have a simple system where an if statement read req.cookies.visits and sees if it is 0 or undefined, then redirects to a password screen. However, a problem arises when the Unblocker module is taken into account. Doing
app.get('/proxy', function(req, res){
if(req.cookies.visits==0||req.cookies.visits==undefined){
res.redirect("/password");
} else {
res.sendFile(__dirname + '/public/index.html');
}
});
doesn't read the cookie for when the request is made (because the route is /proxy/http address). I've tried using express middleware, but doing req.cookie.visits results in an undefined error. Unblocker also has a built in middleware functionality, but the page on npm only shows how to do a response and not a request.
Basically, how could I read a cookie with every request and be compatible with this module?
Check out a full example with middleware :
const express = require('express')
const app = express();
const port = 3000;
var Unblocker = require('unblocker');
const cookieParser = require("cookie-parser");
app.use(cookieParser());
app.use(new Unblocker({prefix: '/proxy/'}));
function checkVisits(req,res,next){
let visits;
try {
visits = req.cookies.visits;
if(isNaN(visits)){
visits = 0;
}
console.log("visits:" + visits);
} catch {
console.log("no visits");
visits = 0;
}finally{
if(visits === 0){
res.redirect("/password");
return;
}
res.visits = parseInt(visits)+1;
res.cookie('visits',res.visits, { maxAge: 900000, httpOnly: true });
return next();
}
}
//add checkVisits(req,res,next) as a middleware
app.get('/index',checkVisits, function (req, res) {
res.send('index visits:'+res.visits);
})
//add checkVisits(req,res,next) as a middleware
app.get('/home',checkVisits, function (req, res) {
res.send('home visits:'+res.visits);
})
app.get('/password', function (req, res) {
res.send('password page');
})
app.listen(port, () => console.log("App listening on port %s!", port))
The checkVisits(req,res,next) middleware will check for visits cookie. If visits cookies is 0 the user will redirected to route /password .

How to send firebase session cookie from server to frontend

The problem that I am facing is that the session cookies created on the server seem to not be available on the browser. I'm using firebase session cookies which can be found here: ( https://firebase.google.com/docs/auth/admin/manage-cookies )
Below is the code I have
Server
index.js
const express = require('express');
const cors = require('cors');
const cookieParser = require('cookie-parser');
const app = express();
app.use(cookieParser());
app.use(cors());
app.use(express.urlencoded({extended: true}));
app.use(express.json());
user.js
userRouter.post('/sessionLogin', (req, res) => {
console.log("Got session login request");
// Get the ID token passed and the CSRF token.
const idToken = req.body.idToken.toString();
// Set session expiration to 5 days.
const expiresIn = 60 * 60 * 24 * 5 * 1000;
fb.auth().createSessionCookie(idToken, {expiresIn})
.then((sessionCookie) => {
const options = {maxAge: expiresIn, httpOnly: true, secure: true};
res.setHeader('Cache-Control', 'private');
res.cookie('__session', sessionCookie, options);
return res.send(JSON.stringify({status: 'success'}));
}).catch((error) => {
res.status(401).send('UNAUTHORIZED REQUEST!');
});
});
Frontend
fb.auth.signInWithEmailAndPassword(email, password).then(user => {
return user.user.getIdToken().then(idToken => {
console.log(idToken);
//document.cookie = '__session=' + idToken + ';max-age=3600';
return ref.postIdTokenToSessionLogin(idToken);
});
})
When I use postman I'm able to see the session created as expected
postman session picture
My server and frontend are hosted on different domains. I can't seem to wrap my head around this any ideas would be highly appreciated.
Thanks,
You cannot share cookies across domains (unless they are subdomains). See Cross-Domain Cookies for a related discussion.

request session not persistent - express-session

I'm trying to have a session containing user data in the node.js/express FW.
I'm using express-session. I'm not using session store yet.
I have 2 pages in the client (angular) where I iterate between - Login and Dashboard. The idea is to create the session after successful login, then routing to the dashboard page. In the dashboard page I have an anchor with routinlink to the login:
<a [routerLink]="['/login']" >BackToLogin</a>
When navigating back to the loginPage (when activating a route), I execute a service with an end-point to the express server which check if the request has a session with a request in it (I expect it to be). The problem is that I see that the session is not the same session (the id changes)
See my code:
Node.js side - server.js file:
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const cors = require('cors');
const session = require ('express-session');
var cookieParser = require('cookie-parser');
const SESS_NAME = 'sid';
app.use(session({
name:SESS_NAME,
key: 'user_sid',
resave:false,
saveUninitialized:false,
secure: process.env.NODE_ENV ==="production",
secret:'<some random text>',
cookie:{
httpOnly: true,
secure: process.env.NODE_ENV ==="production",
expires: 60000
}
}));
app.use(bodyParser.text());
app.use(bodyParser);
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(cors()); //No limitation for test reasons
app.use(cookieParser());
//disabled on purpose
//var sessionManagement = require('./middleware/sessionManagement');
// API
app.use("/", require("./api/v1/routes.js"))//This file includes:
/*
const express = require('express');
const router = express.Router();
router.use("/login", require('./login'));
router.use("/session", require('./session'));
module.exports = router;
*/
...etc
app.listen(config.port, () => console.log(`Process ${process.pid}: Listening on port ${config.port}`));
login.js on the server: responsible for validating user and store user data in session:
const express = require('express');
const router = express.Router();
const schema = require('./objectSchemaJson.schema.json');
const scehmaCheck = require('../../middleware/checkForSchema')(schema);//this is
a schema check (middleware) - if suceeded continue (next)
const storeSession = (req, dataResult) =>
{
if (<dataResult return with valid use data>) //This is "where the magic happanes"
{
req.session.user = {
username: <get userName from dataResult>,
ID: <Get ID from dataResult>,
Role: <Get Role from dataResult>
}
}
}
router.use("/", scehmaCheck, (req, res, next) => {
return GetUserDataFROmDB(req.body).then((dataResult) => { //reaching the DB - not mentioned here on purpose
storeSession(req, dataResult); // This is where the session set with user data
res.status(200).json(dataResult);
}).catch((err) => {
next({
details: err
})
});
});
module.exports = router;
This is the end point on the server that responsible for getting the session - session.js - This is where the problem appears - the res.session has a session ID which is different that the one I created after the login
const express = require('express');
const router = express.Router();
hasSession : function(req, res) //This is where the problem appears - the res.session has a session ID which is different that the one I created after the login
{
if (req.session.user)
{
res.status(200).json(
{
recordsets: [{Roles: req.session.Roles, UserName: req.session.user.username}]
});
}
else{
res.status(200).json({});
}
}
router.use("/", (req, res, next) => { return sessionManagement.hasSession(req, res, next)});
module.exports = router;
Client side:
//HTML:
<div>
<label>Username:</label>
<input type="text" name="username" [(ngModel)]="userName" />
</div>
<div>
<label>Password:</label>
<input type="password" name="password" [(ngModel)]="password"/>
</div>
<div>
<button (click)="login()">Login</button>
</div>
//COMPONENT:
login()
{
this.srv.login(this.userName, this.password).subscribe(result =>
{
if (<result is valid>)
{
this.router.navigate(['/dashboard']);
}
}
);
}
//This reach the node.js endpoint and routing to the session.js end point - it is executes when the router-outlet activated in the app.component:
/*
onActivate(componentRef : any)
{
if (componentRef instanceof LoginComponent)
{
componentRef.getSession();
}
}
*/
getSession() : void
{
this.sessionService.getSession().subscribe( result =>
{
if (<result is valid>)
{
this.router.navigate(['/dashboard']);
}
});
}
I found a similar question on github - no solution yet:
https://github.com/expressjs/session/issues/515
but it might be a cookie <-> server configuration issue.
Found the problem - the root cause was that the client didn't send a cookie when making an httprequest.
2 things needed to be done in order to solve the problem:
1. CORS Definition
Set the CORS definition to creadentials: true along with the origin (the host name of the client, which is probably with a different port\hostname):
app.use(cors({
origin: config.origin,
credentials: true
}));
2. Set crendentials
For every http rest method (get and post, in my case) add withCredentials property with a value of true:
return this.http.get<any>(<path>, { withCredentials: true })
or
return this.http.post<any>(<path>, <body>, { withCredentials:true })

Node Express setting cookies

I may be misunderstanding here.
I have a node server running at localhost:3000, and a React app running at localhost:8080.
The React app is making a get request to the node server - my server code for this looks like:
const cookieParser = require('cookie-parser');
const crypto = require('crypto');
const express = require('express');
const app = express();
app.use(cookieParser());
app.get('/', function (req, res) {
let user_token = req.cookies['house_user']; // always empty
if (user_token) {
// if the token exists, great!
} else {
crypto.randomBytes(24, function(err, buffer) {
let token = buffer.toString('hex');
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8080');
res.cookie('house_user', token, {maxAge: 9000000000, httpOnly: true, secure: false });
res.send(token);
});
}
});
app.listen(3000, () => console.log('Example app listening on port 3000!'))
I'm trying to set the house_user token, so that I can later keep track of requests from users.
However, the token is not being set on the user (request from localhost:8080) - the house_user token is always empty (in fact, req.cookies is entirely empty). Do I need to do something else?
I just tried the code below (and it worked). As a reminder, you can just paste this in myNodeTest.js, then run node myNodeTest.js and visit http://localhost:3003. If it does work, then it probably means you're having CORS issues.
[EDIT] withCredentials:true should do the trick with axios.
axios.get('localhost:3000', {withCredentials: true}).then(function (res) { console.log(res) })
const express = require('express')
const cookieParser = require('cookie-parser')
const crypto = require('crypto');
const port = 3003
app.use(cookieParser());
app.get('/', function (req, res) {
let user_token = req.cookies['house_user']; // always empty
if (user_token) {
// if the token exists, great!
} else {
crypto.randomBytes(24, function(err, buffer) {
let token = buffer.toString('hex');
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8080');
res.cookie('house_user', token, {maxAge: 9000000000, httpOnly: true, secure: true });
res.append('Set-Cookie', 'house_user=' + token + ';');
res.send(token);
});
}
});
app.get('/', (request, response) => {
response.send('Hello from Express!')
})
app.listen(port, (err) => {
if (err) {
return console.log('something bad happened', err)
}
console.log(`server is listening on ${port}`)
})
Making my comment into an answer since it seemed to have solved your problem.
Since you are running on http, not https, you need to remove the secure: true from the cookie as that will make the cookie only be sent over an https connection which will keep the browser from sending it back to you over your http connection.
Also, remove the res.append(...) as res.cookie() is all that is needed.

Resources