In localhost, deserializeUser is being called, and there was no problem.
But after we deploy our app to ec2 using nginx(react), deserializeUser is never called.
const express = require("express");
const cors = require("cors");
const path = require("path");
const morgan = require("morgan");
const bodyParser = require("body-parser");
const dotenv = require("dotenv");
const cookieParser = require("cookie-parser");
const session = require("express-session");
const passport = require("passport");
const flash = require("connect-flash");
const authRouter = require("./routes/auth");
const calendarRouter = require("./routes/calendar");
const healthPillRouter = require("./routes/healthPill");
const { sequelize } = require("./models");
const passportConfig = require("./passport");
dotenv.config();
const app = express();
sequelize
.sync()
.then(() => {
console.log("db 연결 성공");
})
.catch(console.error);
passportConfig(passport);
app.set("view engine", "pug");
app.use(morgan("combined"));
app.use(cors({ origin: "http://13.124.67.98", credentials: true }));
app.use("/", express.static(path.join(__dirname, "public")));
app.use(express.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(
session({
resave: false,
saveUninitialized: false,
secret: process.env.COOKIE_SECRET,
cookie: {
httpOnly: true,
secure: false,
},
})
);
app.use(passport.initialize());
app.use(passport.session());
app.use("/", authRouter);
app.use("/", calendarRouter);
app.use("/", healthPillRouter);
app.listen(4000, () => {
console.log("실행중");
});
app.set("port", process.env.PORT || 8001);
// if (process.env.NODE_ENV === "production") {
// app.use(morgan("combined"));
// } else {
// app.use(morgan("dev"));
// }
app.use(express.static(path.join(__dirname, "public")));
app.use(cookieParser(process.env.COOKIE_SECRET));
const sessionOption = {
resave: false,
saveUninitialized: false,
secret: process.env.COOKIE_SECRET,
cookie: {
httpOnly: true,
secure: false,
},
};
// if (process.env.NODE_ENV === "production") {
// sessionOption.proxy = true;
// sessionOption.cookie.secure = true;
// }
app.use(session(sessionOption));
app.use(flash());
this is our app.js
and the bottom is our router code.
<auth.js>
const express = require("express");
const passport = require("passport");
const bcrypt = require("bcrypt");
const { isLoggedIn, isNotLoggedIn } = require("./middlewares");
const { User, Cycle } = require("../models");
const router = express.Router();
//회원가입
router.post("/api/auth/register", isNotLoggedIn, async (req, res) => {
const {
userName,
userEmail,
userPassword,
userBirth,
userWeight,
userHeight,
firCycleStart,
firCycleEnd,
meanCycle,
meanPeriod,
userAlcohol,
} = req.body;
try {
//exUser 존재 시
const exUser = await User.findOne({ where: { userEmail } });
if (exUser) {
return res.send("이미 가입된 이메일입니다");
}
//비밀번호 암호화
const hash = await bcrypt.hash(userPassword, 12);
//users 테이블에 사용자 정보 저장
await User.create({
userName: userName,
userEmail: userEmail,
userPassword: hash,
userBirth: userBirth,
userWeight: userWeight,
userHeight: userHeight,
meanCycle: meanCycle,
meanPeriod: meanPeriod,
userAlcohol: userAlcohol,
});
const loginUser = await User.findOne({
attributes: ["id"],
where: {
userEmail: userEmail,
},
});
//시작일 정보를 입력했을 때만 주기정보 저장
if (firCycleStart) {
await Cycle.create({
bleedStart: firCycleStart,
bleedEnd: firCycleEnd,
userId: loginUser.id,
});
}
return res.status(201).json({ completed: true });
} catch (error) {
console.error(error);
return next(error);
}
});
//로그인 성공 시 json 형식으로 사용자 이름 send
router.post("/api/auth/login", isNotLoggedIn, async (req, res, next) => {
passport.authenticate("local", (authError, user, info) => {
if (authError) {
console.log(authError);
return next(authError);
}
if (!user) {
return res.send(info.message);
}
return req.login(user, (loginError) => {
if (loginError) {
console.error(loginError);
return next(loginError);
}
return res.json({id: user.id, name: user.userName});
});
})(req, res, next);
});
//로그아웃
router.get("/api/auth/logout", isLoggedIn, async (req, res) => {
console.log(req.user.id);
req.logout();
req.session.destroy();
console.log("로그아웃");
return res.status(200).send("로그아웃 되었습니다");
});
module.exports = router;
<calendar.js>
const express = require("express");
const { isLoggedIn } = require("./middlewares");
const { User, Date, Cycle } = require("../models");
const router = express.Router();
const moment = require("moment");
const Sequelize = require("sequelize");
const Op = Sequelize.Op;
//캘린더 디테일 페이지 POST
//로그인한 사용자의 id는 req.user.id로 가져올 수 있다
router.post("/api/main/date", isLoggedIn, async (req, res) => {
const {
date,
cycleStart,
cycleEnd,
isSex,
isProtection,
isControl,
dateMood,
//★ 프런트 처리 미완 ★
dateCondition,
dateMemo,
} = req.body;
try {
//사용자가 입력한 정보를 dates 테이블에 입력
//upsert 기준이 (date+userId)여야하는데 sequelize는 FK를 composite key로 사용 불가... if문 쓰는 수 밖에?
const exDate = await Date.findOne({
where: { date: date, userId: req.user.id },
});
//이미 존재하던 날짜 정보면 update
if (exDate) {
await Date.update(
{
date: date,
isSex: isSex,
isProtection: isProtection,
isControl: isControl,
dateMood: dateMood,
dateCondition1: dateCondition,
//★ 프런트 처리 미완 ★
dateCondition2: 0,
dateCondition3: 0,
dateMemo: dateMemo,
userId: req.user.id,
},
{
where: { date: date, userId: req.user.id },
}
);
} else {
//새로운 날짜 정보면 create
await Date.create({
date: date,
isSex: isSex,
isProtection: isProtection,
isControl: isControl,
dateMood: dateMood,
dateCondition1: dateCondition,
//★ 프런트 처리 미완 ★
dateCondition2: 0,
dateCondition3: 0,
dateMemo: dateMemo,
userId: req.user.id,
});
}
//사용자가 입력한 정보를 cycles 테이블에 입력
//cycleStart cycleEnd 동시에 존재하는 경우는 없게 프런트에서 처리 완료
const exCycle = await Cycle.findOne({
where: {
bleedStart: { [Op.ne]: null },
bleedEnd: null,
userId: req.user.id,
},
});
//bleedStart만 있고 bleedEnd는 없는 이전 기록이 존재하는 경우
if (exCycle) {
if (cycleStart) {
//잘못된 입력. 이전 기록의 cycleEnd를 미리 설정해야 함.
res.send("최근 생리 종료일을 먼저 입력해야 합니다.");
} else if (cycleEnd) {
//사용자가 cycleEnd를 설정: cycles 테이블 bleedEnd 업데이트
await Cycle.update(
{
bleedEnd: cycleEnd,
},
{
where: {
bleedStart: { [Op.ne]: null },
bleedEnd: null,
userId: req.user.id,
},
}
);
return res.status(200).json({ completed: true });
}
} else {
//이전 기록이 존재하지 않는 경우
if (cycleStart) {
//사용자가 cycleStart를 설정: cycles 테이블 bleedStart 저장
await Cycle.create({
bleedStart: cycleStart,
userId: req.user.id,
});
return res.status(200).json({ completed: true });
} else if (cycleEnd) {
//사용자가 cycleEnd를 설정: cycles 테이블 bleedEnd 저장, bleedStart = bleedEnd - cycles.meanPeriod로 계산 후 저장
const userInfo = await User.findOne({
attributes: ["meanPeriod"],
where: { id: req.user.id },
});
await Cycle.create({
//★ meanPeriod를 입력 안 한 사용자일때? ★
bleedStart: moment(cycleEnd, "YYYY-MM-DD")
.subtract(userInfo.meanPeriod, "d")
.format("YYYY-MM-DD"),
bleedEnd: cycleEnd,
userId: req.user.id,
});
return res.status(200).json({ completed: true });
} else {
return res.status(200).json({ completed: true });
}
}
} catch (error) {
console.error(error);
return next(error);
}
});
//캘린더 디테일 페이지 GET
//입력된 정보가 있으면 보내주고, 없으면 "입력된 정보가 없습니다."
router.get("/api/main/", isLoggedIn, async (req, res) => {
//날짜는 req.body로 받아옴
const date = req.query.Date_send;
try {
const exDate = await Date.findOne({
where: { date: date, userId: req.user.id },
});
if (exDate) {
res.send(exDate);
} else {
res.send("입력된 정보가 없습니다.");
}
} catch (error) {
console.error(error);
return next(error);
}
});
router.get("/api/main/today", isLoggedIn, async (req, res) => {
//날짜는 req.body로 받아옴
const date = req.query.Today_send;
try {
const exDate = await Date.findOne({
where: { date: date, userId: req.user.id },
});
if (exDate) {
res.send(exDate);
} else {
res.send("입력된 정보가 없습니다.");
}
} catch (error) {
console.error(error);
return next(error);
}
});
module.exports = router;
login and register have no problem, but I couldn't use any functions needed login session or cookies. How I solve this problem?
Since the release of Google Chrome 80 on February 4, 2020, the default value of Cookie's SameSite property has been changed from None to Lax.
In other words, if you send an intersection request, not the same domain address, the cookie will not work.
The solution was to change the cookie settings to sameSite none in backend app.js.
But if you change it, you have to put the attribute secure true, and it was only possible in https.
Even if it is not https, if front and backend are using the same domain, there is no problem.
I believe that applying the domain will not result in an error, because eventually you have to have a domain to apply for https
Related
This is a rest API that fetches movie data, similar to Netflix. I've created a login route for new users and registered users, however, when I test the API on postman and attempt to log in as a user I get an error message:
Errors:
Expect Output:
I want the user to log in successfully
The Problem:
I can't find anything wrong with the code despite reference errors
Question:
What kind of data and hash arguments do I require? What is causing the problem?
Code:
source code
model.js
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
let movieSchema = mongoose.Schema({
Title: {
type: String,
required: true
},
Description: {
type: String,
required: true
},
Genre: {
Name: String,
Description: String
},
Director: {
Name: String,
Bio: String,
Birth: String
},
Actors: [String],
ImageURL: String,
Featured: Boolean
});
let userSchema = mongoose.Schema({
Username: {
type: String,
required: true
},
Password: {
type: String,
required: true
},
Email: {
type: String,
required: true
},
Birthday: Date,
FavoriteMovies: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Movie'
}]
});
// HASH THE PASSWORD BEFORE SAVING THE USER MODEL TO THE DATABASE
userSchema.statics.hashPassword = (password) => {
return bcrypt.hashSync(password, 10);
};
// COMPARE THE HASHED PASSWORD IN THE DATABASE WITH THE ONE PROVIDED BY THE USER WHEN THEY LOG IN
userSchema.methods.validatePassword = function(password) {
return bcrypt.compareSync(password, this.Password);
};
// SCHEMA FOR DIRECTORS
let directorSchema = mongoose.Schema({
Name: {
type: String,
required: true
},
Bio: {
type: String,
required: true
},
Birth: {
type: String,
required: true
}
})
/// SCHEMA FOR GENRES
let genreSchema = mongoose.Schema({
Name: {
type: String,
required: true
},
Description: {
type: String,
required: true
}
})
// CREATE MODELS FROM SCHEMAS
let Movie = mongoose.model('Movie', movieSchema);
let User = mongoose.model('User', userSchema);
let Director = mongoose.model('Director', directorSchema);
let Genre = mongoose.model('Genre', genreSchema);
// EXPORT MODELS
module.exports.Movie = Movie;
module.exports.User = User;
module.exports.Director = Director;
module.exports.Genre = Genre;
passport.js
const passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
Models = require('./models/models'),
passportJWT = require('passport-jwt');
let Users = Models.User,
JWTStrategy = passportJWT.Strategy,
ExtractJWT = passportJWT.ExtractJwt;
passport.use(new LocalStrategy({
usernameField: 'Username',
passwordField: 'Password'
}, (username, password, callback) => {
console.log(`${username} ${password}`);
Users.findOne({
Username: username
}, (error, user) => {
if (error) {
console.log(error);
return callback(error);
}
if (!user) {
console.log('incorrect username');
return callback(null, false, {
message: 'Incorrect username or password.'
});
}
if (!user.validatePassword(password)) {
console.log('incorrect password');
return callback(null, false, {
message: 'Incorrect password.'
});
}
console.log('finished');
return callback(null, user);
});
}));
passport.use(new JWTStrategy({
jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken(),
secretOrKey: 'your_jwt_secret'
}, async(jwtPayload, callback) => {
try {
const user = await Users.findById(jwtPayload._id);
return callback(null, user);
} catch (error) {
return callback(error);
}
}));
index.js
const express = require('express'),
morgan = require('morgan'),
bodyParser = require('body-parser'),
uuid = require('uuid'),
mongoose = require('mongoose'),
Models = require('./models/models'),
{
check,
validationResult
} = require('express-validator');
const app = express();
// SCHEMAS
const Movies = Models.Movie;
const Users = Models.User;
const Genres = Models.Genre;
const Directors = Models.Director;
mongoose.connect('mongodb://localhost:27017/myMovies', {
useNewUrlParser: true,
useUnifiedTopology: true
}); // DATABASE Option 1: Local DB
// mongoose.connect(process.env.CONNECTION_URI, { useNewUrlParser: true, dbName: "myMoviesDB", useUnifiedTopology: true }); // REMOTE DATABASE Option 2: Remote DB
// MIDDLEWARE
app.use(bodyParser.json());
// LOGGING MIDDLEWARE
const cors = require('cors');
// ALL ORIGINS ARE ALLOWED TO ACCESS THE API ENDPOINTS
app.use(cors());
// CETAIN ORIGINS ARE ALLOWED TO ACCESS THE API ENDPOINTS:
// let allowedOrigins = ['http://localhost:8080', 'http://testsite.com']
// app.use(cors({
// origin: (origin, callback) => {
// if (!origin) return callback(null, true); // i don't understand this line
// if (allowedOrigins.indexOf(origin) === -1){ // specific origin not in allowedOrigins list
// let message = 'The CORS policy for this application doesn’t allow access from origin ' + origin;
// return callback(new Error(message), false);
// }
// return callback(null, true);
// }
// }));
// ERROR HANDLING MIDDLEWARE
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});
// INPUT VALIDATION
const userValidation = [
check('Username', 'Username is required').isLength({
min: 5
}),
check('Username', 'Username contains non alphanumeric characters - not allowed.').isAlphanumeric(),
check('Password', 'Password is required').not().isEmpty(),
check('Email', 'Email does not appear to be valid').isEmail()
];
// INPUT VALIDATION FOR MOVIES
const movieValidation = [
check('Title', 'Title is required').not().isEmpty(),
check('Description', 'Description is required').not().isEmpty(),
check('Genre', 'Genre is required').not().isEmpty(),
check('Director', 'Director is required').not().isEmpty(),
check('Actors', 'Actors is required').not().isEmpty(),
check('ImageURL', 'ImageURL is required').not().isEmpty(),
check('Featured', 'Featured is required').not().isEmpty()
];
//AUTHENTICATION
require('./auth')(app);
const passport = require('passport');
const {
Passport
} = require('passport');
require('./passport');
// LOGS REQUESTS TO THE CONSOLE
app.use(morgan('common'));
app.use(express.static('public'));
//ROUTING / HOME
app.get("/", (req, res) => {
res.send('Hello there! Welcome to myMovies');
})
// LIST OF ALL MOVIES
app.get('/users', passport.authenticate('jwt', {
session: false
}), (req, res) => {
Users.find()
.then((allUsers) => {
res.status(201).json(allUsers);
})
.catch((err) => {
console.error(err);
res.status(500).send(`Error: ${err}`);
});
});
// ADD User
app.post('/users', userValidation, (req, res) => {
//check for validation errors
let errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({
errors: errors.array()
});
}
let hashedPassword = Users.hashPassword(req.body.Password);
Users.findOne({
Username: req.body.Username
})
.then((user) => {
if (user) {
return res.status(400).send(`Username ${req.body.Username} already taken.`);
} else {
Users
.create({
Username: req.body.Username,
Password: hashedPassword,
Email: req.body.Email,
Birthday: req.body.Birthday
})
.then((new_user) => {
res.status(201).json(new_user)
})
.catch((error) => {
console.error(error);
res.status(500).send(`Error: ${error}`);
})
}
})
.catch((error) => {
console.error(error);
res.status(500).send(`Error: ${error}`);
});
});
// REMOVE USER
app.delete('/users/:Username', passport.authenticate('jwt', {
session: false
}), (req, res) => {
// ONLY ALLOWS USERS TO DELETE THEIR OWN ACCOUNT
if (req.user.Username !== req.params.Username) {
res.status(403).json('You are not authorized to delete this user.');
} else {
Users.findOneAndRemove({
Username: req.params.Username
})
.then((user) => {
if (!user) {
res.status(400).send(`${req.params.Username} was not found!`);
} else {
res.status(200).send(`${req.params.Username} was deleted`);
}
}).catch((err) => {
console.error(err);
res.status(500).send(`Error: ${err}`);
});
}
});
// GET USER INFO BY USERNAME
app.get('/users/:Username', passport.authenticate('jwt', {
session: false
}), (req, res) => {
Users.findOne({
Username: req.params.Username
}).then((user) => {
res.json(user);
}).catch((err) => {
console.error(err);
res.status(500).send(`Error: ${err}`);
});
});
// UPDATE USER INFO
app.put('/users/:Username', passport.authenticate('jwt', {
session: false
}), userValidation, (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(422).json({
errors: errors.array()
});
}
Users.findOneAndUpdate({
Username: req.params.Username
}, {
$set: {
Username: req.body.Username,
Password: req.body.Password,
Email: req.body.Email,
Birthday: req.body.Birthday
}
}, {
new: true
}, // This line ensures that the updated document is returned
(err, updateUser) => {
if (err) {
console.error(err);
res.status(500).send(`Error: ${err}`);
} else {
res.json(updateUser); // Return the updated document
}
})
});
// MOVIES
// GET: fetches a list of all movies
// app.get('/movies', (req, res) => {
// Movies.find()
// .then((movies) => {
// res.status(201).json(movies);
// })
// .catch((error) => {
// console.error(error);
// res.status(500).send(`Error: ${error}`);
// });
// });
app.get('/movies', passport.authenticate('jwt', {
session: false
}), (req, res) => {
Movies.find()
.then((movies) => {
res.status(201).json(movies);
})
.catch((error) => {
console.error(error);
res.status(500).send(`Error: ${error}`);
});
});
// GET: fetches movies by title
app.get('/movies/:Title', passport.authenticate('jwt', {
sesson: false
}), (req, res) => {
Movies.findOne({
Title: req.params.Title
})
.then((movie) => {
res.json(movie);
}).catch((err) => {
console.error(err);
res.status(500).send('Error: ' + err);
});
});
// ADD MOVIE TO FAVORITES LIST OF USER
app.post('/users/:Username/movies/:MovieID',
passport.authenticate({
session: false
}),
(req, res) => {
if (req.user.Username !== req.params.Username) {
res.status(403).json('Not allowed to add movie to another user\'s favorites list');
} else {
Users.findOneAndUpdate({
Username: req.params.Username
}, {
$addToSet: {
FavoriteMovies: req.params.MovieID
}
}, {
$push: {
FavoriteMovies: req.params.MovieID
}
}, {
new: true
}, // This line ensures that the updated document is returned
(err, updated_User) => {
if (err) {
console.error(err);
res.status(500).send(`Error:${err}`)
} else {
res.json(updated_User)
}
});
}
});
// REMOVE MOVIE FROM FAVORITES
app.delete('/users/:Username/movies/:MovieID', passport.authenticate('jwt', {
session: false
}), (req, res) => {
// ONLY ALLOQWS USERS TO REMOVE MOVIES FROM THEIR OWN FAVORITES
if (req.user.Username != req.params.Username) {
res.status(403).json('Not allowed to remove movie from another user\'s favorites list');
} else {
Users.findOneAndUpdate({
Username: req.params.Username
}, {
pull: {
FavoriteMovies: req.params.MovieID
}
}, {
new: true
}) // THIS LINE ENSURES THAT THE UPDATED DOCUMENT IS RETURNED
.then((updatedUser) => {
res.status(200).send(`Favorite movie removed from ${updatedUser.Username}`);
})
.catch((err) => {
console.error(err);
res.status(500).send(`Error: ${err}`);
});
}
});
// DIRECTORS
// RETURN A LIST OF ALL DIRECTORS BY NAME (BIO, BIRTHYEAR)
app.get('/directors/:Name', passport.authenticate('jwt', {
session: false
}), (req, res) => {
Directors.findOne({
Name: req.params.Name
}).then((director) => {
res.json(director);
}).catch((err) => {
console.error(err);
res.status(500).send('Error: ' + err); // 500: INTERNAL SERVER ERROR
})
})
// GENRES
// GET: returns all genres
app.get('/genres', (req, res) => {
Genres.find().then((genre) => {
res.status(201).json(genre);
}).catch((err) => {
console.error(err);
res.status(400).send('Error: ' + err);
})
})
// RETURNS GENRE BY NAME
app.get('/genres/:Name', (req, res) => {
Genres.findOne({
Name: req.params.Name
}).then((genreName) => {
res.status(201).json(genreName)
}).catch((err) => {
console.error(err);
res.status(500).send('Error: ' + err);
})
})
// DOCUMENTATION ROUTE
app.get('/documentation', (req, res) => {
res.sendFile('public/documentation.html');
});
// SERVER & HEROKU
const port = process.env.PORT || 8081;
app.listen(port, '0.0.0.0', () => {
console.log(`Listening on Port ${port}`);
});
Research:
I've searched and read through most of the common questions related to the issue, but none helped.
Question #1
Question #2
Thank you in advance for taking the time to answer my question.
I send a cookie to client side but i can't get
In my browser there is no cookies
auth.js:
export const Login = async (req, res, next) => {
try {
const user = await User.findOne({
email: req.body.email,
});
if (!user) return next(createError(404, "User Not Found"));
const CorrectPassword = await bcrypt.compare(
req.body.password,
user.password
);
if (!CorrectPassword)
return next(createError(400, "Wrong password or email"));
const token = jwt.sign({
id: user._id,
isAdmin: user.isAdmin
},
process.env.JWT_SEC
)
const {password, isAdmin, ...others} = user._doc
res.cookie("access_token", token, {
maxAge:900000,
httpOnly:true,
}).status(200).json(others);
} catch (err) {
next(err);
}
};
Login.jsx React:
const handleClick = async (e) => {
e.preventDefault();
dispatch(loginStart());
try {
const res = await axios.post("http://localhost:8800/api/auth/login", {
email,
password,
});
dispatch(loginSuccess(res.data));
navigate("/");
} catch (err) {
dispatch(loginFailure());
console.log(err);
}
};
This is my code
How to fix this?
You have to edit secure option to be false.
secure: true is a recommended option. However, it requires an https-enabled website, i.e., HTTPS is necessary for secure cookies:
app.use(
session({
store: new MongoStore({
mongooseConnection: mongoose.connection,
url: 'mongodb://localhost:27017/test'
}),
secret: "mysecret-ssss",
resave: false,
saveUninitialized: false,
cookie: {
maxAge: 1000 * 60 * 60 * 2,
sameSite: true,
secure: false // in development
}
})
);
The problem is that I cannot get the cookie in the server. It is set in the browser (I can see it in devtools). When using Postman it works fine (including the middleware). But for some reason when making the request from the browser to the middleware (authMiddleware) the token comes back undefined.
Anyone with any ideas would be great. I have been stuck on this for days! :O
App.js (server)
const express = require('express');
const cookieParser = require('cookie-parser');
const authRouter = require('./routes/authRoutes');
const cocktailRouter = require('./routes/cocktailRoutes');
require('dotenv').config();
const cors = require('cors');
const app = express();
mongoose.connect(process.env.DBURI, { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true })
.then((result) => {
console.log('listening');
app.listen(5000)
})
.catch((err) => console.log(err));
//middleware
app.use(express.static('public'));
app.use(express.json());
app.use(cookieParser());
app.use(cors({ origin: true, credentials: true }));
// ROUTER MIDDLEWARE
app.use('/api/cocktails', cocktailRouter);
app.use('/auth', authRouter);
authMiddleware
const User = require('../models/User');
require('dotenv').config();
//check user is authorized
const requireAuth = (req, res, next) => {
const token = req.cookies.jwt;
console.log('TOKEN', token)
//check json web token exists and is verified
if (token) {
jwt.verify(token, process.env.SECRET_KEY, (err, decodedToken) => {
if (err) {
// res.redirect('/auth/login');
console.log('not logged in')
} else {
console.log('logged in ')
next();
}
})
} else {
// res.redirect('/auth/login');
console.log('not logged in again')
}
};
module.exports = { requireAuth };
cocktailRoute
This is where I am implementing the requireAuth middleware:
const router = Router();
const { requireAuth } = require('../middleware/authMiddleware');
const { cocktail_get, cocktail_post, cocktail_put, cocktail_delete } = require('../controllers/cocktailController');
router.get('/', requireAuth, cocktail_get);
router.post('/', cocktail_post);
router.put('/:cocktailId', cocktail_put);
router.delete('/:cocktailId', cocktail_delete);
module.exports = router;
authController
const jwt = require('jsonwebtoken');
//handle errors function
const handleErrors = (err) => {
const { message } = err;
const errors = { email: '', password: '' }
//incorrect email
if (message === 'Incorrect email') {
errors.email = 'Invalid email';
};
//incorrect password
if (message === 'Incorrect password') {
errors.password = 'Invalid password';
};
//duplicate error = 11000
if (err.code === 11000) {
errors.email = 'That email is already registered';
return errors;
}
//validation errors
if (message.includes('user validation failed')) {
Object.values(err.errors).forEach(({ properties }) => {
errors[properties.path] = properties.message;
})
};
return errors;
}
//create jwt token
const maxAge = 3 * 24 * 60 * 60; // 3 days in seconds
const createToken = (id) => {
return jwt.sign({ id }, process.env.SECRET_KEY, { expiresIn: maxAge })
};
module.exports.register_post = async (req, res) => {
const { name, email, password } = req.body;
try {
const user = await User.create({ name, email, password });
const token = createToken(user._id);
res.cookie('jwt', token, { httpOnly: false, maxAge: maxAge * 1000 });
res.status(201).json({ user: user._id, token });
} catch (err) {
const errors = handleErrors(err);
res.status(400).json({ errors });
}
};
module.exports.login_post = async (req, res) => {
const { email, password } = req.body;
try {
const user = await User.login(email, password);
const token = createToken(user._id);
res.cookie('jwt', token, { httpOnly: false, maxAge: maxAge * 1000, secure: false });
res.status(200).json({ user: user._id });
} catch (err) {
const errors = handleErrors(err);
res.status(400).json({ errors });
}
};
module.exports.logout_get = (req, res) => {
console.log(res.cookie('jwt', '', { maxAge: 1 }))
res.cookie('jwt', '', { maxAge: 1 });
res.redirect('/');
}
FRONTEND (React)
LoginPage
const LoginPage = () => {
const [values, setValues] = useState({ email: '', password: '' });
const handleChange = (event) => {
const { name, value } = event.target;
setValues({ ...values, [name]: value });
};
const handleLoginFormSubmit = (event) => {
event.preventDefault();
const { email, password } = values;
try {
fetch(`${process.env.REACT_APP_API_URL}/auth/login`, {
method: "POST",
withCredentials: true,
credentials: 'include',
headers: { "Content-Type": "application/json", "Accept": "application/json" },
body: JSON.stringify({
email, password
})
})
} catch (err) { if (err.request) { console.log('REQUEST', err.request) } if (err.response) { console.log('RESPONSE', err.response) } }
};
return (
<div>
<h1>Login</h1>
<form onSubmit={handleLoginFormSubmit}>
<input type="text" value={values.email} name="email" placeholder="Enter your email" onChange={handleChange} />
<input type="password" value={values.password} name="password" placeholder="Enter your password" onChange={handleChange} />
<button>Login</button>
</form>
</div>
)
}
export default LoginPage
I am making full stack app and learn from tutorials and videos . I have a problem with GET request to get information about user which is login in the system. I use Postman to check the requests. When I add user with /login , the Postman look user's accesstoken code. I copy his code and paste it in authorization key in headers in Postman and when I change the URL in localhost to /infor to get information about this user and send it. But it say me "Invalid Authentication". I can't find the wrong. I think the problem is in controllers/userCtrl.js in getUser function. Can you help me?
I put the code:
server.js
require('dotenv').config()
const express = require('express')
const mongoose = require('mongoose')
const cors = require('cors')
const fileUpload = require('express-fileupload')
const cookieParser = require('cookie-parser')
const app = express()
app.use(express.json())
app.use(cookieParser())
app.use(cors())
// Use temp files instead of memory for managing the upload process.
app.use(fileUpload({
useTempFiles: true
}))
// Routes
app.use('/user', require('./routes/userRouter'))
// Connect to Mongodb
const URL = process.env.MONGO_URL
mongoose.connect(URL,{
useCreateIndex: true,
useFindAndModify: false,
useNewUrlParser: true,
useUnifiedTopology: true
}, err =>{
if(err) throw err;
console.log('Connected to MongoDB')
})
const PORT = process.env.PORT || 5000
app.listen(PORT, () => {
console.log('Server is running on port', PORT)
})
.env
MONGO_URL = ***********
ACCESS_TOKEN_SECRET = ***********
REFRESH_TOKEN_SECRET = *************
routes/userRouter.js
require('dotenv').config()
const express = require('express')
const mongoose = require('mongoose')
const cors = require('cors')
const fileUpload = require('express-fileupload')
const cookieParser = require('cookie-parser')
const app = express()
app.use(express.json())
app.use(cookieParser())
app.use(cors())
// Use temp files instead of memory for managing the upload process.
app.use(fileUpload({
useTempFiles: true
}))
// Routes
app.use('/user', require('./routes/userRouter'))
// Connect to Mongodb
const URL = process.env.MONGO_URL
mongoose.connect(URL,{
useCreateIndex: true,
useFindAndModify: false,
useNewUrlParser: true,
useUnifiedTopology: true
}, err =>{
if(err) throw err;
console.log('Connected to MongoDB')
})
const PORT = process.env.PORT || 5000
app.listen(PORT, () => {
console.log('Server is running on port', PORT)
})
models/userModel.js
const mongoose = require('mongoose')
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true,
trim: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true,
},
role: {
type: Number,
default: 0
},
cart: {
type: Array,
default: []
}
}, {
timestamps: true
})
module.exports = mongoose.model('Users', userSchema)
middleware/auth.js
const jwt = require('jsonwebtoken')
const auth = (req, res, next) => {
try{
const token = req.header("Authorization")
if(!token) return res.status(400).json({ msg: "Invalid Authentication" })
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, user) => {
if(!err) return res.status(400).json({msg: "Invalid Authentication" })
req.user = user
next()
})
} catch (err) {
return res.status(500).json({msg: err.message})
}
}
module.exports = auth
controllers/userCtrl.js
const Users = require('../models/userModel')
const bcrypt = require('bcrypt')
const jwt = require('jsonwebtoken')
const userCtrl = {
register: async (req, res) => { // async before a function means one simple thing: a function always returns a promise.
try{
const { name, email, password } = req.body
const user = await Users.findOne({ email }) // wait until the promise resolves
if(user) return res.status(400).json({msg: "The email already exists"})
if(password.length < 6)
return res.status(400).json({msg: "Password is at least 6 characteres long."})
//Password encryption
const passwordHash = await bcrypt.hash(password, 10)
const newUser = new Users({
name, email, password: passwordHash
})
// save mongodb
await newUser.save()
//then create jsonwebtoken to authentication
const accesstoken = createAccessToken({ id: newUser._id })
const refreshtoken = createRefreshToken({ id: newUser._id })
res.cookie('refreshtoken', refreshtoken, {
httpOnly: true,
path: '/user/refresh_token'
});
res.json({accesstoken})
} catch(err){
return res.status(500).json({msg: err.message})
}
},
login: async (req, res) => {
try{
const {email, password} = req.body;
const user = await Users.findOne({email})
if(!user) return res.status(400).json({msg: "User does not exist."})
const isMatch = await bcrypt.compare(password, user.password)
if(!isMatch) return res.status(400).json({msg: "Incorrect password"})
// if login success, create access token and refresh token
const accesstoken = createAccessToken({ id: user._id })
const refreshtoken = createRefreshToken({ id: user._id })
res.cookie('refreshtoken', refreshtoken, {
httpOnly: true,
path: '/user/refresh_token'
});
res.json({accesstoken})
} catch(err){
return res.status(500).json({msg: err.message})
}
},
logout: async (req, res)=> {
try{
res.clearCookie('refreshtoken', {path: '/user/refresh_token'})
return res.json({msg: "Logged out"})
}catch(err){
return res.status(500).json({msg: err.message})
}
},
refreshToken: (req, res) => {
try{
const rftoken = req.cookies.refreshtoken
if(!rftoken) return res.status(400).json({msg: "Please login or Register"})
jwt.verify(rftoken, process.env.REFRESH_TOKEN_SECRET, (err, user) => {
if(err) return res.status(400).json({msg: "Please login or Register"})
const accesstoken = createAccessToken({id: user.id})
res.json({ accesstoken })
})
}catch (err) {
return res.status(500).json({msg: err.message})
}
},
getUser: async (req, res) => { // problem
try{
const user = await (await Users.findById(req.user.id)).isSelected('-password')
if(!user) return res.status(400).json({ msg: "Useer does not exist."})
res.json(req.user)
}catch (err) {
return res.status(500).json({msg: err.message})
}
}
}
const createAccessToken = (user) => {
return jwt.sign(user, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '1d' })
}
const createRefreshToken = (user) => {
return jwt.sign(user, process.env.REFRESH_TOKEN_SECRET, { expiresIn: '7d' })
}
module.exports = userCtrl
For your middle ware for getting the token (auth function)
const { authorization } = req.headers
if (!authorization) {
console.log('[No Authorization Code]');
return res.status(401).send({ message: 'Unauthorized' });
}
if (!authorization.startsWith('Bearer')) {
console.log('[Authorization need to start with Bearer]')
return res.status(401).send({ message: 'Unauthorized' });
}
const split = authorization.split('Bearer ')
if (split.length !== 2) {
console.log('[Invalid Authorization Param')
return res.status(401).send({ message: 'Unauthorized' });
}
const token = split[1] //this is your token to use with jwt.verify
When you sending the token in postman, select Bearer Token
When you start creating your frontend, the codes should be equivalent to the following fetch request
fetch('/api/path', { method: 'GET', headers: { "Authorization": `Bearer ${token}`}}).(res => res.json())
May change method to your desire method (e.g get or post), and the token will be the the jwt token
I am new to programming so I don't know why this happens. Users are reporting app crashes when logging in. The app is hosted on the Heroku platform with MongoDB. Can anyone tell me what the problem might be? P.S. I don't know if there is any other way to show the code, so I'll just paste it below. The user and password for the MongoDB connection string are changed for obvious reasons.
//jshint esversion:6
const express = require("express");
const bodyParser = require("body-parser");
const ejs = require("ejs");
const MongoClient = require("mongodb").MongoClient;
const app = express();
const uri =
"mongodb+srv://user:password#cluster0.4wwjt.mongodb.net/anketa?retryWrites=true&w=majority";
const client = new MongoClient(uri, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
app.set("view engine", "ejs");
app.use(
bodyParser.urlencoded({
extended: true,
})
);
app.use(express.static("public"));
app.get("/", (req, res) => {
res.render("login");
});
app.get("/link", (req, res) => {
res.render("link");
});
app.get("/confirm", (req, res) => {
res.render("confirm");
});
app.get("/noMoreLinks", (req, res) => {
res.render("noMoreLinks");
});
app.post("/link", (req, res) => {
const username = req.body.username;
const password = req.body.password;
client.connect((err) => {
const users = client.db("anketa").collection("users");
const links = client.db("anketa").collection("links");
users.findOne({ user: username }, (err, foundUser) => {
if (err) {
console.log(err);
} else {
if (foundUser && foundUser.pass === password) {
const unvisitedLinksLength = foundUser.unvisitedLinks.length;
const currentTime = Date.now();
if (
foundUser.timeFinished &&
currentTime - foundUser.timeFinished < 18 * 60 * 60 * 1000
) {
res.render("comeTomorrow", {
headline: "Probajte opet za nekoliko sati",
});
return;
}
if (unvisitedLinksLength === 20) {
links.findOne({ id: 1 }, (err, foundLink) => {
if (err) {
console.log(err);
} else {
res.render("link", {
link: foundLink.path,
});
users.update(
{ user: foundUser.user },
{ $set: { currentLink: foundLink.id } }
);
}
});
return;
}
if (unvisitedLinksLength < 20 && unvisitedLinksLength > 0) {
let randomNumber = Math.floor(Math.random() * unvisitedLinksLength);
links.findOne(
{ id: foundUser.unvisitedLinks[randomNumber] },
(err, foundLink) => {
res.render("link", {
link: foundLink.path,
});
users.update(
{ user: foundUser.user },
{ $set: { currentLink: foundLink.id } }
);
}
);
return;
}
if (unvisitedLinksLength === 0 && foundUser.currentLink < 21) {
links.findOne({ id: foundUser.currentLink }, (err, foundLink) => {
res.render("link", {
link: foundLink.path,
});
});
return;
}
if (foundUser.currentLink === 0 && unvisitedLinksLength === 0) {
res.render("noMoreLinks");
return;
}
}
}
});
});
});
app.post("/confirm", (req, res) => {
const username = req.body.username;
client.connect((err) => {
const users = client.db("anketa").collection("users");
users.findOne({ user: username }, (err, foundUser) => {
const currentLinkId = foundUser.currentLink;
const unvisitedLinks = foundUser.unvisitedLinks;
const newUnvisitedLinks = unvisitedLinks.filter(
(linkId) => linkId !== currentLinkId
);
users.update(
{ user: foundUser.user },
{
$set: { unvisitedLinks: newUnvisitedLinks, timeFinished: Date.now() },
}
);
if (unvisitedLinks.length === 0) {
users.update({ user: foundUser.user }, { $set: { currentLink: 0 } });
return;
}
});
});
res.render("login");
});
let port = process.env.PORT;
if (port == null || port == "") {
port = 3000;
}
app.listen(port, () => {
console.log("server started successfully");
});