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,
})
Related
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
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.
Background
I am tryng to do an simple authtentication system using jwt and mysql+sequelize here
What I had being able to do so far
I am being able to login and generate a token after that.
Problem
After being logged in, when I try to check the jwt token with a middleware on a particular route, it simply says that the token was not found.
What I tried to do
Since the token should be found at the req.headers['x-access-control'] or ['authorization'] I tried to console.log(req.headers) when I call the middleware but there is nothing about these two headers... Then I think that the problem might be there, but I have no idea on how to solving this :(
Pieces of code
This is the middleware (userController.js) which I use to login (signIn) and to verify the token (verifyToken)
var bcrypt = require("bcryptjs");
var jwt = require("jsonwebtoken");
const config = require("../config/config");
const User = require("../models").User;
module.exports = {
signIn(req, res) {
console.log("Sign-In");
User.findOne({
where: {
email: req.body.email
}
})
.then(user => {
if (!user) {
return res.status(404).send("User not found");
}
var passwordIsValid = bcrypt.compareSync(
req.body.password,
user.password
);
if (!passwordIsValid) {
return res
.status(401)
.send({
auth: false,
accessToken: null,
reason: "Invalid Password"
});
}
var token = jwt.sign({ id: user.id }, config.secret, {
expiresIn: 86400 // expires in 24 hours
});
res.status(200).send({ auth: true, accessToken: token });
})
.catch(err => {
res.status(500).send("Error =>" + err);
});
},
verifyToken(req, res) {
console.log(req.headers);
let token =
req.body.token ||
req.query.token ||
req.headers["x-access-token"] ||
req.headers["authorization"];
if (!token) {
return res.status(403).send({
auth: false,
message: "No token provided"
});
}
jwt.verify(token, config.secret, (err, decoded) => {
if (err) {
return res.status(500).send({
auth: false,
message: "Fail to Authentication. Error -> " + err
});
}
req.userId = decoded.id;
next();
});
}
};
and here is the app.js file with the Routes
const express = require("express");
const app = express();
const port = 3000;
const Sequelize = require("sequelize");
const sequelize = new Sequelize("mydb", "root", "Metin.234", {
host: "localhost",
dialect: "mysql"
});
sequelize
.authenticate()
.then(() => {
console.log("Connection with sequelize has been established successfully");
})
.catch(err => {
console.error("Unable to connect", err);
});
const userController = require("./controllers").user;
var bodyParser = require("body-parser");
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.set("view engine", "ejs");
app.use(express.static(__dirname + "/public"));
app.get("/", (req, res) => {
res.render("home");
});
app.get("/register", (req, res) => {
res.render("register");
});
app.get("/login", (req, res) => {
res.render("login");
});
app.post("/login", userController.signIn);
app.get("/showAll", userController.verifyToken);
app.listen(port, () => {
console.log("server started!");
});
Thanks in advance, any help is appreciated! =D
EDIT
I saved the token on a variable called globalToken, so then it could be used on the verify token function (EDIT 2 and it works fine for what I want), however I am wondering on how can I get the value of accesstoken that is inside the response object that I am sending after the login proccess.
I am using nodejs as backend server while my frontend is in angular. I need to call the register router and for that i am sending the data from frontend in json fromat which is perfect but since i am trying to passport authentication here, i took passport authentication code from another github repository in which node was also using an engine so that's why res.render was used in it. But now i am finding it difficult to remove this particular line from the register router and if i try to delete it or just use res.sendStatus(500) it still does not work and i get 500 error. What could be the solution of this problem?
var express = require('express');
var mongoose = require('mongoose');
var bodyparser = require('body-parser');
var cors = require('cors');
var session = require('cookie-session');
var flash = require('connect-flash');
var passport = require('passport');
var bcrypt = require('bcryptjs');
require('./config/passport')(passport);
var User = require('./models/User');
var app = express();
app.use(bodyparser.json());
app.use(cors());
var expiryDate = new Date(Date.now() + 60 * 60 * 1000) // 1 hour
app.use(session({
name: 'session',
keys: ['key1', 'key2'],
cookie: {
secure: true,
httpOnly: true,
domain: 'example.com',
path: 'foo/bar',
expires: expiryDate
}
}))
app.set('port', process.env.port || 3000);
app.use(passport.initialize());
app.use(passport.session());
// Connect flash
app.use(flash());
// Global variables
app.use(function(req, res, next) {
res.locals.success_msg = req.flash('success_msg');
res.locals.error_msg = req.flash('error_msg');
res.locals.error = req.flash('error');
next();
});
var db = mongoose.connect("mongodb://localhost:27017/server", {
useNewUrlParser: true
}, function(err, response) {
if (err) {
console.log('There is error in connecting with mongodb');
}
console.log('Connection has been established.');
});
app.get('/', (req, res) => {
res.send("hello");
});
//Trying registering with passport
app.post('/register', (req, res) => {
console.log(req.body);
const { firstname, lastname, email, password } = req.body;
let errors = [];
if (errors.length > 0) {
res.render('register', {
errors,
name,
email,
password,
password2
});
} else {
User.findOne({ email: email }).then(user => {
if (user) {
errors.push({ msg: 'Email already exists' });
res.render('register', {
errors,
firstname,
lastname,
email,
password
});
} else {
const newUser = new User({
firstname,
lastname,
email,
password
});
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) throw err;
newUser.password = hash;
newUser
.save()
.then(user => {
req.flash(
'success_msg',
'You are now registered and can log in'
);
res.redirect('/login');
})
.catch(err => console.log(err));
});
});
}
})
.catch(err => console.log(err))
}
});
//end
app.post('/login', (req, res, next) => {
console.log(req.body);
passport.authenticate('local', {
successRedirect: '/dashboard',
failureRedirect: '/login',
failureFlash: true
})(req, res, next);
});
app.listen(app.get('port'), function(err, response) {
console.log("Server is running");
});
You are using res.render() in the callback function in app.post('/register', callback). If res.render() is called it will look for a template (probably in a views directory). If you have not specified which engine to use you will get that error.