I work with reactjs and nodejs(express) . so what i want to do is store a token generated using JWT in a cookie and send it to the front end.
The problem is that the cookie is not displayed in the browser !
front end work on port 3000 (localhost:3000)
for the backend it works on port 3006 (localhost:3006)
here my app.js (server side)
var express = require("express");
var path = require("path");
var cookieParser = require("cookie-parser");
require("dotenv").config();
var indexRouter = require("./routes/index");
const Pool = require("pg");
const cors = require("cors");
var app = express();
app.use(
cors({
origin: "http://localhost:3000",
optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
})
);
//preventing Cors error
app.use((req, res, next) => {
res.header("Content-Type", "application/json;charset=UTF-8");
res.header("Access-Control-Allow-Credentials", true);
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
next();
});
app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, "public")));
app.use("/api", indexRouter);
module.exports = app;
my index.js
var express = require('express');
const { Register, Login, Check} = require('../controllers/Users.controller');
var router = express.Router();
const auth = require('../middleware/auth')
// routes for inscription
router.post('/inscrire', Register)
router.post('/login', Login)
router.get('/islogged', auth, Check)
module.exports = router;
and here is the controller
const Login = (req, res, next) => {
const { email, motdepass } = req.body.values;
pool
.query("SELECT * FROM public.user WHERE email = $1", [email])
.then((result) => {
if (result.rowCount == 0) {
return res.status(401).json({ error: "Utilisateur non trouvé !" });
} else {
bcrypt
.compare(motdepass, result.rows[0].motdepass)
.then((valid) => {
if (!valid) {
return res
.status(401)
.json({ error: "Mot de passe incorrect !" });
}
const token = jwt.sign(
{ userId: result.rows[0]._id },
process.env.TOKEN,
{ expiresIn: "24h" }
);
res.cookie("jwt", token, {
httpOnly: false,
secure: false,
maxAge: 6000000 * 60,
});
res.status(200).json({
userId: result.rows[0]._id,
email: result.rows[0].email,
token: token,
});
})
.catch((error) => {
res.status(500).json({ message: "Mot de pass invalide !" });
console.log(error);
});
}
})
.catch((error) => res.status(500).json({ message: "invalide password" }));
};
in the front side we have the function :
const handleLogin = (values) => {
axios
.post("http://localhost:3006/api/login", {
values,
withCredentials: true,
credential: 'include'
})
.then((response) => {
setUser(response.data.email);
setError(true);
auth.login(user);
// navigate('/')
})
.catch((err) => {
setError(false);
});
}
Note : i can see the set-cookie at response Headers
Related
I'm having trouble with express-sessions while running on localhost. When I call req.session in development it only retrieves part of my session cookie. But it works fine in production. my app.js file is as follows:
const path = require("path");
const express = require('express');
const cookieParser = require('cookie-parser')
const session = require('express-session');
const MongoDBStore = require('connect-mongodb-session')(session);
const mongoose = require('./db/mongoose');
const PORT = process.env.PORT;
const retrieveUserIdFromRequest = require("./middleware/get-user.middleware");
const MONGODB_URI = `mongodb+srv://${process.env.MONGO_USER}:${process.env.MONGO_PW}#cluster0.mongodb.net/test`
const app = express();
var store = new MongoDBStore({
uri: MONGODB_URI,
collection: 'sessions',
});
app.use((req, res, next) => {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, Authorization"
);
res.setHeader(
"Access-Control-Allow-Methods",
"GET, POST, PATCH, PUT, DELETE, OPTIONS"
);
next();
});
const usersRoutes = require("./routes/users");
const loginsRoutes = require("./routes/logins");
const accountsRoutes = require("./routes/accounts");
const projectsRoutes = require("./routes/projects");
app.use(express.json());
app.use(express.urlencoded({extended: true}));
app.use(cookieParser())
app.use("/", express.static(path.join(__dirname,'angular')));
var hour = 3600000
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
store: store,
cookie: {
path: '/',
httpOnly: true,
secure: true,
maxAge: hour,
expires: new Date(Date.now() + hour),
sameSite: true
}
}));
app.use(retrieveUserIdFromRequest);
app.use((req, res, next) => {
console.log('req.session.user app.js', req.session)
next();
});
app.use('/api/users' , usersRoutes)
app.use('/api/logins' , loginsRoutes)
app.use("/api/accounts", accountsRoutes);
app.use("/api/projects", projectsRoutes);
app.use((req, res, next) => {
res.sendFile(path.join(__dirname, "angular", "index.html"));
});
app.listen(PORT, () => {
console.log("Server is listening on port " + PORT);
})
module.exports = app;
my login route is as follows:
exports.login = async (req, res) => {
await User.findOne({ email: req.body.email })
.then(async(user) => {
let obj = user.toJSON()
obj.instances = await user.buildAccess()
const fetchedUser = await obj;
if (!fetchedUser) {
res.status(200).json({
message: 'Could not find e-mail in database.'
})
} else {
if(user.activated){
loginAndBuildResponse(req, res, fetchedUser);
req.session.isLoggedIn = true
req.session.user = {
_id: fetchedUser.id,
email: fetchedUser.email,
access: fetchedUser.access
}
req.session.save()
} else {
res.status(200).json({
message: 'Your account has not been activated. Please check your email for your activation link.'
})
}
}
});
}
While in development, when I call req.session it does not retrieve the session.isLoggedIn or session.user data as I would expect, or as it does in production. Does anyone have any ideas why this might be the case or where i am going wrong?
I am trying to access the req.user that passport creates when doing a google o auth strategy. I can access the req.user in the routes file below, but when I try to access it in my userController file it is showing up as undefined.
Why is user accessible in routes file but not userController?
googleAuthRoutes.js:
const passport = require('passport');
const requireLogin = require('../middlewares/requireLogin')
const cors = require('cors');
const axios = require('axios');
const Template = require('../models/Template');
const corsOptions ={
origin: true,
credentials:true, //access-control-allow-credentials:true
optionSuccessStatus:200
}
module.exports = app => {
app.get('/auth/google',
passport.authenticate('google', {
scope: ['profile', 'email']
}));
app.get(
'/auth/google/callback',
passport.authenticate('google'),
(req, res) => {
res.redirect('/dashboard');
}
);
app.post('/templates/create', async (req, res) => {
const { template, body } = req.body
console.log(req.user)
const newTemplate = new Template({
template: template,
body: body,
_user: req.user.id
})
try {
await newTemplate.save()
return res.status(200).json({
message: "Successfully saved template"
})
} catch (err) {
return console.log(err)
}
});
app.get('/api/logout', cors(), (req, res) => {
req.logout();
res.redirect('http://localhost:3000');
});
app.get('/api/current_user', (req, res) => {
res.send(req.user);
})
}
when I call the res.send(req.user) here above it sends the user no problem
But it is undefined with the /templates/create route middleware.
the console.log(req.user) is coming back as undefined??
index.js:
const express = require('express');
const cors = require('cors')
const mongoose = require('mongoose');
const cookieSession = require('cookie-session');
const passport = require('passport');
const keys = require('./config/keys');
const bodyParser = require('body-parser')
require("dotenv").config();
require('./models/GoogleUserModel'); // the user model must be placed before this services passport// this must be ran after requiring model bcuz this needs the model. ORDER
require('./models/UserModel');
require('./services/passport');
const corsOptions ={
origin:'http://localhost:3000',
credentials: true, //access-control-allow-credentials:true
optionSuccessStatus:200
}
const app = express();
app.use(cors(corsOptions))
mongoose.connect(keys.mongoURI, {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true
})
mongoose.connection.on('error', () => {
throw new Error (`unable to connect to database: ${keys.mongoURI}`)
});
app.use(bodyParser.json())
app.use(express.urlencoded( { extended: true }))
app.use(
cookieSession({
maxAge: 30 * 24 * 60 * 60 * 1000,
keys: [keys.cookieKey]
})
)
app.use(passport.initialize());
app.use(passport.session());
require('./routes/userRoutes')(app);
require('./routes/googleAuthRoutes')(app);
require('./routes/messageRoutes')(app);
app.use((err, req, res, next) => {
if (err.name === 'UnauthorizedError') {
res.status(401).json({"error" : err.name + ": " + err.message})
} else if (err) {
res.status(400).json({"error" : err.name + ": " + err.message})
console.log(err)
}
})
const PORT = process.env.PORT || 5000;
app.listen(PORT);
Again, Why is the req.user available in the app.get to /api/current_user but available in a post request to /templates/create?
Im trying to add the user.id to the schema when it saves so i can retrieve each template by the user.id and not show everyone everybody elses templates lol
I am logging in successfully, however, I am getting unauthorized when I'm trying to access my authenticated-only route. I don't understand what I am doing wrong here, it successfully logs me in and returns the user, where am I wrong?
Here's my code:
This here is basically the server configuration for the backend.
server.js
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const cors = require('cors')
const passport = require('passport')
// passport
const cookieParser = require('cookie-parser')
const session = require('cookie-session')
const { COOKIE_NAME } = require('./client/src/common/config')
const app = express();
// Bodyparser Middleware
app.use(bodyParser.json());
// DB Config
const db = require ('./config/keys').mongoURI;
// Connect to MongoDB
mongoose
.connect(db, {useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify: false})
.then(() => console.log('Mongo DB Connected...'))
.catch(err => console.log(err));
// CORS
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
const secret = process.env.APP_SECRET
const env = process.env.NODE_ENV || 'development'
const isLocal = env === 'development'
/* Session Setup */
app.use(cookieParser()) // read cookies (needed for auth)
if (!isLocal) {
app.set('trust proxy', 1)
}
app.use(
session({
httpOnly: false,
name: COOKIE_NAME,
keys: [secret],
secure: !isLocal,
maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days
})
)
/* Session management with Passport */
require('./passport')(passport)
app.use(passport.initialize())
app.use(passport.session())
// Register Schema
require('./models/User')
// Insert some default users
// require('./config/_insertDefaultUsers')
const patients = require('./routes/api/patients');
const auth = require('./routes/api/auth');
const drugs = require('./routes/api/drugs');
const trainees = require('./routes/api/trainees')
// Use Routes
app.use('/api/patients', patients);
app.use('/api/drugs', drugs);
app.use('/api/trainees', trainees)
app.use('/api/auth', auth);
app.use(cors())
// Connect to deployment port or localhost
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`Server started on port ${port}`));
access.js this is a basic middleware to check for authentication, so I can add it in my routes
const ROLES = require('.././client/src/common/roles')
/** Access middleware to ensure user is allowed to access certain routes */
const AccessMiddleware = {
hasAccess: (req, res, next) => {
if (!req.isAuthenticated()) {
req.session.redirectTo = req.originalUrl
return res.status(401).json({ success: false, error: 'unauthorized' })
}
next()
},
hasAdminAccess: (req, res, next) => {
if (!req.isAuthenticated() || req.user.role !== ROLES.ADMIN) {
req.session.redirectTo = req.originalUrl
return res.status(401).json({ success: false, error: 'unauthorized' })
}
next()
},
}
module.exports = AccessMiddleware
auth.js route and this is basically the authentication API which allows me to login and so on
const express = require('express');
const router = express.Router();
const passport = require('passport')
const AccessMiddleware = require('../../config/access')
const errorResponse = (res, error) => {
res.status(400).json({ success: false, error })
}
router.get('/test', (req, res) => {
res.json({ success: true, message: 'Test API route working fine!' })
})
router.get('/authenticated-only', AccessMiddleware.hasAccess, (req, res) => {
res.json({ success: true, message: 'You have auth access!' })
})
router.get('/admin-only', AccessMiddleware.hasAdminAccess, (req, res) => {
res.json({ success: true, message: 'You have admin access!' })
})
router.post('/login', (req, res, next) => {
const { email, password } = req.body
if (!email || !password) {
return errorResponse(res, 'Invalid credentials')
}
// Authenticate the user using the credentials provided
passport.authenticate('local', { session: true }, function (err, user) {
if (err) {
return errorResponse(res, 'Invalid credentials')
}
// When using passport with callback, we have to manually call req.login to set the Cookie
req.login(user, async () => {
res.json({ success: true, user })
})
})(req, res, next)
})
module.exports = router
exports.errorResponse = errorResponse
For anyone looking for a solution:
const secret = "secrethere"
app.use(cookieParser("secrethere"))
The issue seems to have been that I was not using the same secret within the server file.
What I'm trying to do is get json data from "/get" to "/", but I'm not sure why axios keeps giving my a 403 error. From what I read, axios isn't the issue, but cors is. I tried all the ways that people are recommending, but nothing is working. What I'm I doing wrong within my code? I'm also seeing "Referrer Policy: strict-origin-when-cross-origin" in the Header, but I'm not sure if that's what's causing this issue.
const express = require("express");
const mysql = require("mysql2");
var app = express();
const bodyparser = require("body-parser");
const cors = require("cors");
const cookieParser = require("cookie-parser");
const expressSession = require("express-session")({
secret: "secret",
resave: false,
saveUninitialized: false,
});
const passport = require("passport");
const jwt = require('jsonwebtoken');
app.use(passport.initialize());
app.use(passport.session());
app.use(bodyparser.json());
app.use(cors());
app.use(bodyparser.urlencoded({ extended: false }));
app.use(express.json());
app.use(cookieParser());
app.use(expressSession);
const axios = require('axios');
var mysqlConnection = mysql.createConnection({
host: "localhost",
user: "root",
password: "",
database: "sys",
multipleStatements: true,
});
mysqlConnection.connect((err) => {
if (!err) console.log("DB connection succeded.");
else
console.log(
"DB connection failed \n Error : " + JSON.stringify(err, undefined, 2)
);
});
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Methods', 'DELETE, PUT, GET, POST');
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
app.listen(5000, () =>
console.log("Express server is runnig at port no : 5000")
);
app.get("/get", authenticateToken, (req, res) => {
jwt.verify(req.token, 'secretpassword', (err) => {
if (err) {
res.sendStatus(403);
} else {
mysqlConnection.query("SELECT * FROM sys.jobs", (err, rows) => {
if (rows === undefined) {
res.send("Hello World!");
} else {
res.send(rows);
}
});
}
});
});
app.get("/", authenticateToken, (req, res) => {
jwt.verify(req.token, 'secretpassword', async (err) => {
if (err) {
res.sendStatus(403);
} else {
let response = await axios.get('http://localhost:5000/' +'get', {
withCredentials: true,
headers: {
'Access-Control-Allow-Origin': '*',
'Authorization': `Bearer
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNjAwOTIyMTQ0LCJleHAiOjE2MDg2OTgxNDR9.aRsw-
jEQJ-7mlO10nBKA5VT3IL7P0b9T9K0C8aT8sUs`
}
});
res.send(response.data);
}
});
});
app.post("/login", async (req, res) => {
try {
const { email } = req.body;
mysqlConnection.query("SELECT * FROM sys.users WHERE email = ?", [email], async (error, results) => {
const id = results[0].id;
const token = jwt.sign({ id }, "secretpassword", {
expiresIn: '90d'
});
const cookieOptions = {
expires: new Date(
Date.now() + 90 * 24 * 60 * 60 * 1000
),
secure: false,
httpOnly: true
}
res.cookie('jwt', token, cookieOptions);
res.status(200).redirect("/get");
});
}
catch (error) {
console.log(error);
}
}
);
function authenticateToken(req, res, next) {
const bearerHeader = req.cookies.jwt;
if (typeof bearerHeader !== 'undefined') {
req.token = bearerHeader;
next();
} else {
res.sendStatus(403);
}
}
use cors module
const cors = require('cors')
//... your app instance code here
app.use(cors())
I've made this app using the MERN stack and am facing problems while deploying to Heroku. I have a userContext.js file which handles authentication of users. It has a function which makes a post request to the server which in development is on localhost:80. This has been working fine in development and the request is successful. After deploying the same request stalls and fails. I don't understand how to get a response from the request. Any help is really appreciated. Thanks, Hatim
server.js
const express = require("express"),
http = require("http"),
app = express(),
server = http.createServer(app),
bodyParser = require("body-parser"),
mongoose = require("mongoose"),
passport = require("passport"),
cors = require("cors");
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cors());
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "DELETE, PUT, GET, POST");
res.header(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept"
);
next();
});
//IMPORT MODELS
require("./models/products");
require("./models/user");
//MONGOOSE CONNECT
mongoose.Promise = global.Promise;
mongoose
.connect(
process.env.MONGODB_URI || `mongodb://localhost:27017/technicalKwt`,
{
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false
}
)
.then(() => console.log("MONGODB Connected"))
.catch(err => {
console.log(err);
});
//SET FOR PRODUCTION
if (process.env.NODE_ENV === "production") {
app.use(express.static("client/build"));
const path = require("path");
app.get("*", (req, res) => {
res.sendFile(path.resolve(__dirname, "client", "build", "index.html"));
});
}
// Passport middleware
app.use(passport.initialize());
// Passport config
require("./config/passport")(passport);
require("./api/products")(app);
require("./api/transaction")(app);
require("./api/users")(app);
require("./api/genPDF")(app);
const port = process.env.PORT || 80;
server.listen(port, () => {
console.log(`Listening on port ${port}`);
});
users.js
app.post("/login", (req, res) => {
console.log(req.body);
const email = req.body.email;
const password = req.body.password;
User.findOne({ email }).then(user => {
if (!user) {
return res.status(401).json({ message: "Invalid Credentials" });
}
// Check password
bcrypt.compare(password, user.password).then(isMatch => {
if (isMatch) {
// User matched
// Create JWT Payload
const payload = {
id: user.id,
name: user.name
};
// Sign token
jwt.sign(
payload,
keys.secretOrKey,
{
expiresIn: 31556926 // 1 year in seconds
},
(err, token) => {
res.json({
success: true,
token: "Bearer " + token,
payload
});
}
);
} else {
return res.status(401).json({ message: "Invalid Credentials" });
}
});
});
});
loginUser function in userContext.js
function loginUser(dispatch, login, password, history, setIsLoading, setError) {
setIsLoading(true);
if (!!login && !!password) {
axios
.post('http://localhost:80/login', {
email: login,
password,
})
.then(res => {
localStorage.setItem('id_token', res.data.payload.id);
localStorage.setItem('name', res.data.payload.name);
setIsLoading(false);
dispatch({ type: 'LOGIN_SUCCESS' });
history.push('/app/products');
})
.catch(err => {
setIsLoading(false);
dispatch({ type: 'LOGIN_FAILURE' });
});
} else {
dispatch({ type: 'LOGIN_FAILURE' });
setIsLoading(false);
}
}
I'm making some assumptions about your app here.
On Heroku I believe you cannot use './' anymore. In your server.js try using the built-in '__dirname':
require(__dirname + "/models/users")
Second issue: Your userContext.js is presumably on the client-side now, as in a person views this on their browser. The path to your routes is no longer localhost:80 it's your heroku domain (something.com) so your axios post request needs the new url. Try this:
axios
.post('http://' + document.location.hostname + '/login', {
email: login,
password,
})