Why cookieParser doesnt return value? - javascript

I'm using vue, vue-router for my client-side and express, morgan for my server side (MEVN app)
So, at the client i'm setting cookies by using vue-cookies
this.$cookies.set('Login', this.login, new Date(Date.now() + 86400 * 5 * 1000))
this.$cookies.set('Password', this.password, new Date(Date.now() + 86400 * 5 * 1000))
And at the server side i'm using cookieParser
So, at app.js i have such a code
const express = require('express');
const morgan = require('morgan');
const cors = require('cors');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const config = require('./config/config');
const db = require('./controllers/DB');
const mCLogs = require('./modelControllers/Logs');
const mCLogin = require('./modelControllers/Login');
const app = express();
app.use(morgan('combined'));
app.use(bodyParser.json());
app.use(cors());
app.use(cookieParser()); /*cookie parser*/
And, at the file ./modelControllers/Login i have such a code for a GET request
exports.checkLoginSession = async (req, res, next) => {
/*its not all of the code*/
var loginHash = req.cookies['Login'];
console.log(loginHash)
if(loginHash == undefined) {
res.send({
logged: false,
description: "err mes"
});
} else {
res.send({
logged: true,
description: "mes"
});
}
}
and the problem is that the var loginHash = req.cookies['Login']; always return undefined, even when i have "Login" cookie
Addition:
How i call this method:
Client-side and using axios
mounted () {
this.getLoginData()
},
methods: {
async getLoginData () {
const response = await LoginHandler.checkUserLoginSession()
if (response.data.logged === true) {
this.$router.push('/')
} else {
this.errorMessage = response.data.description
}
}
}
LoginHandler.js(client side)
import api from '#/services/api'
export default {
checkUserLoginSession () {
return api().get('/login')
}
}
Server-side /login link in app.js
app.get('/login', mCLogin.checkLoginSession);
app.post('/login', mCLogin.checkUserData);
ADDITION:
It doesnt work when i use such a code with axios API:
import api from '#/services/api'
export default {
checkUserLoginSession () {
return api().get('/login')
}
}
So, when i call checkUserLoginSession app.get('/login') return cookie value undefined, but, if i open link in browser (serverside) localhost:3000/login it's returning correct value
Addition: checkUserData
exports.checkUserData = async (req, res) => {
try {
let login = req.body.login;
let password = req.body.password;
const user = await db.users.findOne({
where: {
Login: login,
Password: password
}
});
if(user == null)
{
res.send({
logged: false,
description: "Пользователь не найден."
});
return;
}
if(user.dataValues.Login == login && user.dataValues.Password == password)
{
res.send({
logged: true,
description: "Авторизация произошла успешно. Сейчас Вас перенаправит!"
});
return;
}
}
catch(ex) {
res.send({
logged: false,
description: "Произошла ошибка на стороне сервера."
});
console.log(ex);
return;
}
}
If i add withCredentials: true to axios.create, server return cookie value, but i've this errors on console line
Access to XMLHttpRequest at 'http://localhost:3000/login' from origin 'http://localhost:8080' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.

Ok guys, i solve my issue.
So, the answer is.
Change LoginHandler code:
From:
import api from '#/services/api'
export default {
checkUserLoginSession () {
return api().get('/login')
}
}
To:
import api from '#/services/api'
export default {
checkUserLoginSession () {
return api().get('/login', {withCredentials: true})
}
}
Change app.js
From:
app.use(cors());
To:
app.use(cors({ credentials: true, origin: "http://localhost:8080" }));
Change method checkLoginSession
To:
exports.checkLoginSession = (req, res, next) => {
const { Login, Password } = req.cookies;
//Where Login, Password ... is your cookie name
//console.log(Login)
if(Login == undefined) {
res.send({
logged: false,
description: "Нет сохранённых хешей для авторизации!"
});
} else {
res.send({
logged: true,
description: "Авторизован."
});
}
}
P.S Thanks to all, who tried to help me

Related

Setting and retrieving session works in Postman but not working in browser

I am working with this NodeJS project using express-session to create session for my application. The problem is, when I make a post request to the http://localhost:5500/login, a session is created with additional property userid that I added intentionally. Then, when I use Postman to make a get request to http://localhost:5500/, the application actually receives the session with the property userid and redirect the user to his home page based on the userid is set or not. However, if I make get request to http://localhost:5500/ from a browser like Chrome, my server is not able to get the session with the additional property `userid' that I added when log in successfully and does not redirect my user to his home page. Can anyone explain why this happens please? Thank you
Here is the code of my index.js
`
const express = require("express")
const app = express()
const PORT = process.env.PORT || 5500
const session = require("express-session")
const { routers } = require("./routes/routes")
const mongoose = require("mongoose")
const cookieParser = require("cookie-parser")
const TIME = 1000 * 60 * 5
app.use(cookieParser())
app.use(
session({
secret: "iamnamdo1234567",
saveUninitialized: true,
cookie: { maxAge: TIME, sameSite: "strict" },
resave: false
})
)
const URI = process.env.DB_CONNECTION
app.use(express.urlencoded({ extended: true }))
app.use(express.json())
app.use("/api", routers)
app.get("/", (req, res) => {
let session = req.session.userid
session ? res.status(200).send("Hello my friend, you are logged in") : res.status(400).send("You need to log in")
})
mongoose.connect(URI, { useNewUrlParser: true.valueOf(), useUnifiedTopology: true }, err => {
if (err) {
console.log(err)
} else {
console.log("database connected")
}
})
app.listen(PORT, () => {
console.log(`Go to http://localhost:${PORT}`)
})
`
This is the code of my routes.js
`
const express = require("express")
const route = express.Router()
const { User } = require("../models/User")
const bcrypt = require("bcrypt")
const errorHandler = (type, error) => {
if (type === "register") {
if (error.code === 11000) {
return { message: "Username has been taken" }
} else if (error._message === "User validation failed") {
return { message: error.errors.username?.properties.message || error.errors.password?.properties.message }
}
} else if (type === "login") {
return { message: `${error}` }
}
}
route.post("/register", async (req, res) => {
try {
const { username, password } = req.body
const user = await User.create({ username, password })
res.status(200).send("User has been created successfully")
} catch (error) {
// console.log(error)
let message = errorHandler("register", error)
res.status(400).send(message)
}
})
route.post("/login", async (req, res) => {
const { username, password } = req.body
try {
const user = await User.findOne({ username })
if (!user) {
throw (new Error().message = "Username not found")
}
const checkPassword = await bcrypt.compare(password, user.password)
if (checkPassword === false) {
throw (new Error().message = "Password is incorrect")
} else {
req.session.userid = user.username
console.log(req.session.userid)
res.status(200).send("Logged in")
}
} catch (error) {
let message = errorHandler("login", error)
res.status(400).send(message)
}
})
route.post("/logout", (req, res) => {
req.session.destroy()
res.redirect("/")
})
module.exports.routers = route
`
I tried to access the session when making get request from the browser
If the session details are visible in Postman but not in the browser, it could be due to a number of reasons, one of them is Cookie policy.
By default, cookies are only sent with requests made to the same origin as the current page. To send cookies with cross-origin requests, you need to set the withCredentials option in Axios. Try this it worked for me
const axios = require('axios');
axios.defaults.withCredentials = true;

axios get req error can not access http only token in next js but workd fine in post main

hy am building an forum in next js using httponly cookie for jwt authantication but get this error
this is my auth middleware
import jwt from "jsonwebtoken";
import createError from "http-errors";
import Cookies from "cookies";
/**
* #param {import('next').NextApiRequest} req
* #param {import('next').NextApiResponse} res
*/
module.exports = (req, res, next) => {
const cookies = new Cookies(req, res);
const token = cookies.get("token");
console.log(token);
// const { cookies } = req;
// const token = cookies.token;
if (!token) return next(createError("Unauthorized"));
try {
const user = jwt.verify(token, process.env.NEXT_PUBLIC_JWT);
req.user = user;
console.log(req.user);
next();
} catch (error) {
return next(createError("Login to gain access "));
}
};
this my api to get questions posted by user
import nextConnect from "next-connect";
import connect from "../../../utils/connectMongo";
import Question from "../../../models/question";
import auth from "../../../middleware/auth";
import Cookies from "cookies";
/**
* #param {import('next').NextApiRequest} req
* #param {import('next').NextApiResponse} res
*/
connect();
const apiRoute = nextConnect({
onError(error, req, res) {
res.status(501).json({ error: ` ${error.message}` });
console.log(error);
},
onNoMatch(req, res) {
res.status(405).json({ error: `Method '${req.method}' Not Allowed` });
},
});
apiRoute.use(auth);
apiRoute.get(async (req, res, next) => {
console.log(req.cookies);
try {
const { _id } = req.user;
console.log(req.user);
let { page, size } = req.query;
const limit = parseInt(size);
const skip = (page - 1) * size;
const question = await Question.find({ user: _id })
.limit(limit)
.skip(skip)
.populate("user", "name picture")
.sort({ createdAt: -1 });
res.status(200).json({
success: true,
data: question,
});
} catch (error) {
console.log(error);
res.status(400).json({ success: false });
}
});
// export const config = {
// api: {
// bodyParser: false, // Disallow body parsing, consume as stream
// },
// };
export default apiRoute;
in postman api works fine but in axios it says Unauthorized form auth middleware
my axios configs are
this is how i made req to server
export async function getStaticProps(page, size) {
const response = await instance.get(
`${baseUrl}/api/question/userquestion`,
{ withCredentials: true }
);
const questions = await response.data.data;
return {
props: {
questions,
},
};
}
enter image description here

CORS Error after deploying react app to netlify with Node/Express backend and MySQL in Heroku

Before I deploy, the app performed fine on localhost. But since I deployed my frontend (react) to Netlify and backend(node/express + mysql) to Heroku, all requests sent from the frontend started to get blocked by CORS policy, with the error message:
"Access to XMLHttpRequest at 'https://xxx.herokuapp.com/login' from origin 'https://xxx.netlify.app' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value 'https://xxx.app/' that is not equal to the supplied origin."
Most importantly, the value of my Access-Control-Allow-Origin header is literally the same as the origin stated.
Originally, I've tried to use a wildcard ("*"), but it seems that due to the withCredential problem, the system just can't allow that kind of vague statement.
I've also seen many people using Netlify.toml to tackle some configuration problems, but seems ineffective for me.
Is it the header's problem? If not, then what is the problem?
I really want to know what I should do to solve this error...
The console window of the app deployed:
Cors Error
My index.js in the server folder:
const express = require('express')
const mysql = require('mysql')
const cors = require('cors')
const session = require('express-session')
const bodyParser = require('body-parser')
const cookieParser = require('cookie-parser')
const port = 3010
const app = express()
app.use(express.json())
app.use(cors({
origin: ["https://xxx.app/"], // the link of my front-end app on Netlify
methods: ["GET", "POST"],
credentials: true
}))
app.use(cookieParser())
app.use(bodyParser.urlencoded({
extended: true
}))
app.use(
session({
key: "userId",
secret: "subscribe",
resave: false,
saveUninitialized: false,
cookie: {
expires: 60 * 60 * 24
},
})
)
app.use((req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "https://xxx.netlify.app/"); // the link of my front-end app on Netlify
res.setHeader(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
res.setHeader(
"Access-Control-Allow-Methods",
"GET, POST, PATCH, DELETE, OPTIONS"
);
res.setHeader('content-type', 'application/json');
next();
});
const db = mysql.createPool({
// create an instance of the connection to the mysql database
host: 'xxx.cleardb.net', // specify host name
user: 'xxx', // specify user name
password: 'xxx', // specify password
database: 'heroku_xxx', // specify database name
})
...
app.get('/login', (req, res) => {
if (req.session.user) {
res.send({
isLoggedIn: true,
user: req.session.user
})
} else {
res.send({
isLoggedIn: false
})
}
})
...
app.listen(process.env.PORT || port, () => {
console.log('Successfully Running server at ' + port + '.')
});
My Frontend:
import React, { useEffect, useState } from 'react'
import '../App.css'
import './HeroSection.css'
import { Link } from 'react-router-dom'
import Axios from 'axios'
function HeroSection() {
Axios.defaults.withCredentials = true
let username = "";
const [name, setName] = useState('');
const [isLoggedIn, setIsLoggedIn] = useState(false)
const [isLoading, setLoading] = useState(true)
...
useEffect(() => {
Axios.get('https://xxx.herokuapp.com/login').then((response) => {
if (response.data.isLoggedIn) {
username = response.data.user[0].username;
}
setIsLoggedIn(response.data.isLoggedIn)
Axios.post('https://xxx.herokuapp.com/getLang', {
username: username,
}).then((response) => {
console.log(response.data);
})
Axios.post('https://xxx.herokuapp.com/getStatus', {
username: username,
}).then(response => {
setName(response.data[0].firstname + " " + response.data[0].lastname);
setLoading(false);
})
})
}, [])
if (!isLoggedIn || isLoading) {
return (
<div>
...
</div>
)
} else {
return (
<div>
...
</div>
)
}
}
export default HeroSection
By the way, I use ClearDB MySQL on Heroku and MySQL WorkBench for the database, which all works fine.
You could debug by doing something like:
const allowList = ["https://yyy.app/"];
// Your origin prop in cors({})
origin: function (origin, callback) {
// Log and check yourself if the origin actually matches what you've defined in the allowList array
console.log(origin);
if (allowList.indexOf(origin) !== -1 || !origin) {
callback(null, true)
} else {
callback(new Error('Not allowed by CORS'))
}
}

Next.JS custom server restarting when trying to use Socket.io, address already in use :::3000

Whenever I try to run the function refreshStock() in an endpoint in one of the API endpoints /api/seller/deactivate it gives me this error:
Error: listen EADDRINUSE: address already in use :::3000
at Server.setupListenHandle [as _listen2] (net.js:1318:16)
at listenInCluster (net.js:1366:12)
at Server.listen (net.js:1452:7)
at C:\Users\***\Documents\GitHub\***\***\.next\server\pages\api\seller\deactivate.js:191:10
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command
It looks like it's trying to restart the server, but it happens after it compiles, is there something I'm doing wrong, I've followed a couple of tutorials on medium, and they give this same type of code, just not ES Modules. I want to use ES Modules because it is what my database functions are written in.
Server.js:
import express from 'express';
import { createServer } from 'http';
import next from 'next';
import models from './server/models';
import { genStock } from './server/lib/functions';
import { Server } from 'socket.io';
const port = parseInt(process.env.PORT || '3000', 10);
const dev = process.env.NODE_ENV !== 'production';
const nextApp = next({ dev });
const nextHandler = nextApp.getRequestHandler();
const app = express();
const server = createServer(app);
const io = new Server(server);
const Users = models.users;
io.use(async (socket, next) => {
const err = new Error('Unauthorized');
err.data = { message: 'Unauthorized, please try again later.' };
try {
if (!socket.handshake.auth.token) return next(err);
let user = await Users.findOne({
where: {
socket_token: socket.handshake.auth.token,
},
});
if (!user) {
console.log('unauthenticated socket');
socket.disconnect();
next(err);
}
await Users.update(
{ socket_id: socket.id },
{
where: {
socket_token: socket.handshake.auth.token,
},
},
);
next();
} catch (e) {
console.log(e);
next(e);
}
});
io.on('connection', async (socket) => {
// Works fine
const stock = await genStock();
socket.emit('updateStock', stock);
});
// Fails with address already in use :::3000
export async function refreshStock() {
const stock = await genStock();
io.emit('updateStock', stock);
}
nextApp.prepare().then(async () => {
app.all('*', (req, res) => nextHandler(req, res));
server.listen(port, () => {
console.log(`> Ready on http://localhost:${port}`);
});
});
This is meant to refresh the stock after a seller deactivates their account and sends all users the new stock.
/api/seller/deactivate
....
await refreshStock();
....
I figured it out, I just split up the WebSocket server and the next.js one. I have whitelisted local IPs that may appear to only allow server-to-server communication. Although I don't think this is full-proof as there is most likely a better way to have this type of communication but for now it works.
/**
* This server cannot be imported in /api folders, it won't work.
* Although it can import other functions
* */
import express from 'express';
import { createServer } from 'http';
import session from 'express-session';
import { Server } from 'socket.io';
import { genStock } from './server/lib/stockFunctions';
import { sessionStore } from './server/lib/session';
import passport from './server/lib/passport';
import models from './server/models';
const authorizedIPs = ['::1', '127.0.0.1', '::ffff:127.0.0.1'];
const Users = models.users;
const app = express();
const httpServer = createServer(app);
const io = new Server(httpServer, {
cors: {
origin: `http://localhost:3000`,
methods: ['GET', 'POST'],
credentials: true,
},
});
const wrap = (middleware) => (socket, next) => middleware(socket.request, {}, next);
io.use(
wrap(
session({
secret: "---",
resave: false,
saveUninitialized: true,
cookie: {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
path: '/',
sameSite: 'lax',
},
store: sessionStore,
}),
),
);
io.use(wrap(passport.initialize()));
io.use(wrap(passport.session()));
io.use(async (socket, next) => {
const err = new Error('Unauthorized');
err.data = { message: 'Unauthorized, please try again later.' };
try {
const user = socket.request.user;
if (!user) return next(err);
await Users.update(
{ socket_id: socket.id },
{
where: {
id: user.id,
},
},
);
next();
} catch (e) {
console.log(e);
next(e);
}
});
io.on('connection', async (socket) => {
const stock = await genStock();
socket.emit('updateStock', stock);
});
app.post('/refresh-stock', async function (req, res) {
const ip = req.ip;
if (!authorizedIPs.includes(ip)) {
console.log(ip);
return res.status(401).json({ success: false });
}
const newStock = await genStock();
io.emit('updateStock', newStock);
return res.status(200).json({ success: true });
});
httpServer.listen(3001);
console.log(`> Websockets ready on http://localhost:3001`);

Express.js Csurf working in postman but not React.js

I'm trying to setup CSRF tokens so that I can do a number of checks before issueing a token to the client to use in future requests.
Taking the guidance from the csurf documentation, I've setup my express route with the following:
const express = require('express');
const router = express.Router({mergeParams: true});
const csurf = require('csurf');
const bodyParser = require('body-parser');
const parseForm = bodyParser.urlencoded({ extended: false });
const ErrorClass = require('../classes/ErrorClass');
const csrfMiddleware = csurf({
cookie: true
});
router.get('/getCsrfToken', csrfMiddleware, async (req, res) => {
try {
// code for origin checks removed for example
return res.json({'csrfToken': req.csrfToken()});
} catch (error) {
console.log(error);
return await ErrorClass.handleAsyncError(req, res, error);
}
});
router.post('/', [csrfMiddleware, parseForm], async (req, res) => {
try {
// this returns err.code === 'EBADCSRFTOKEN' when sending in React.js but not Postman
} catch (error) {
console.log(error);
return await ErrorClass.handleAsyncError(req, res, error);
}
});
For context, the React.js code is as follows, makePostRequest 100% sends the _csrf token back to express in req.body._csrf
try {
const { data } = await makePostRequest(
CONTACT,
{
email: values.email_address,
name: values.full_name,
message: values.message,
_csrf: csrfToken,
},
{ websiteId }
);
} catch (error) {
handleError(error);
actions.setSubmitting(false);
}
Postman endpoint seems to be sending the same data, after loading the /getCsrfToken endpoint and I manually update the _csrf token.
Is there something I'm not doing correctly? I think it may be to do with Node.js's cookie system.
I think your problem is likely to be related to CORS (your dev tools will probably have sent a warning?).
Here's the simplest working back-end and front-end I could make, based on the documentation:
In Back-End (NodeJS with Express) Server:
In app.js:
var cookieParser = require('cookie-parser')
var csrf = require('csurf')
var bodyParser = require('body-parser')
var express = require('express')
const cors = require('cors');
var csrfProtection = csrf({ cookie: true })
var parseForm = bodyParser.urlencoded({ extended: false })
var app = express()
const corsOptions = {
origin: "http://localhost:3000",
credentials: true,
}
app.use(cors(corsOptions));
app.use(cookieParser())
app.get('/form', csrfProtection, function (req, res) {
res.json({ csrfToken: req.csrfToken() })
})
app.post('/process', parseForm, csrfProtection, function (req, res) {
res.send('data is being processed')
})
module.exports = app;
(make sure you update the corsOptions origin property to whatever your localhost is in React.
In Index.js:
const app = require('./app')
app.set('port', 5000);
app.listen(app.get('port'), () => {
console.log('App running on port', app.get('port'));
});
In React:
Create file "TestCsurf.js" and populate with this code:
import React from 'react'
export default function TestCsurf() {
let domainUrl = `http://localhost:5000`
const [csrfTokenState, setCsrfTokenState] = React.useState('')
const [haveWeReceivedPostResponseState, setHaveWeReceivedPostResponseState] = React.useState("Not yet. No data has been processed.")
async function getCallToForm() {
const url = `/form`;
let fetchGetResponse = await fetch(`${domainUrl}${url}`, {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"xsrf-token": localStorage.getItem('xsrf-token'),
},
credentials: "include",
mode: 'cors'
})
let parsedResponse = await fetchGetResponse.json();
setCsrfTokenState(parsedResponse.csrfToken)
}
React.useEffect(() => {
getCallToForm()
}, [])
async function testCsurfClicked() {
const url = `/process`
let fetchPostResponse = await fetch(`${domainUrl}${url}`, {
method: "POST",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"xsrf-token": csrfTokenState,
},
credentials: "include",
mode: 'cors',
})
let parsedResponse = await fetchPostResponse.text()
setHaveWeReceivedPostResponseState(parsedResponse)
}
return (
<div>
<button onClick={testCsurfClicked}>Test Csurf Post Call</button>
<p>csrfTokenState is: {csrfTokenState}</p>
<p>Have we succesfully navigates csurf with token?: {JSON.stringify(haveWeReceivedPostResponseState)}</p>
</div>
)
}
Import this into your app.js
import CsurfTutorial from './CsurfTutorial';
function App() {
return (
<CsurfTutorial></CsurfTutorial>
);
}
export default App;
That's the simplest solution I can make based on the CSURF documentations example. It's taken me several days to figure this out. I wish they'd give us a bit more direction!
I made a tutorial video in case it's of any help to anyone: https://youtu.be/N5U7KtxvVto

Categories

Resources