I am using Express and node for the session management with https. I want to create a session using express so that authentication and the session is made before accessing the data. So I want only admin who can access the books, but the session is undefined. here is my code :
AuthUser.js
export const adminOnly = async (req, res, next) => {
if (!req.session.userId) {
return res.status(401).json({ msg: "You must be logged in" });
}
const admin = await Admin.findOne({
where: {
uuid: req.session.userId,
},
});
if (!admin) return res.status(404).json({ msg: "Akses Terlarang" });
next();
};
Auth.js
export const LoginAdmin = async (req, res) => {
const admin = await Admin.findOne({
where: {
username: req.body.username,
},
});
if (!admin) return res.status(404).json({ msg: "Data not found" });
const match = await argon2.verify(admin.password, req.body.password);
if (!match) return res.status(400).json({ msg: "Wrong Password" });
req.session.userId = admin.uuid;
const uuid = admin.uuid;
const username = admin.username;
const role = admin.role;
res.status(200).json({ uuid, username, role });
};
index.js
import express from "express";
import cors from "cors";
import dotenv from "dotenv";
import db from "./config/Database.js";
import {
BookRoute,
BookTypeRoute,
CategoryRoute,
Denda,
PeminjamanRoute,
PengembalianRoute,
UserRoute,
AdminRoute,
} from "./routes/index.js";
import AuthRoute from "./routes/AuthRoute.js";
import SequelizeStore from "connect-session-sequelize";
import session from "express-session";
dotenv.config();
const app = express();
const sessionStore = SequelizeStore(session.Store);
const store = new sessionStore({
db: db,
});
app.use(
session({
secret: process.env.SESS_SECRET,
resave: false,
saveUninitialized: true,
store: store,
cookie: {
secure: "auto",
},
})
);
app.use(
cors({
credentials: true,
origin: "https://localhost:3000",
})
);
app.use(express.json());
app.use(UserRoute);
app.use(AdminRoute);
app.use(BookRoute);
app.use(AuthRoute);
app.use(CategoryRoute);
app.use(BookTypeRoute);
app.use(Denda);
app.use(PeminjamanRoute);
app.use(PengembalianRoute);
// store.sync();
app.listen(process.env.APP_PORT, () => {
console.log("Server is listening on port 5000");
});
I have tried logging console.log('req.session.userId') and it returns undefined value and when I delete if (!req.session.userId) return res.status(401).json({ msg: "You must be logged in" }); the error is Error: WHERE parameter "uuid" has invalid "undefined" value.
Related
I have a problem with setting and reading cookies. There is a login mask, a user logs in with an email and password, and if successful, his user ID is saved in a cookie (access_token). If I now call further POST or GETs, it is checked whether the token is still up to date. The problem here is that I get an undefined every time I check. What could be the reason? Thank you very much!
verifyToken.js
import jwt from "jsonwebtoken";
export const verifyToken = (req, res, next) => {
const token = req.cookies.access_token;
console.log("verifyToken: " + token); //undefined
if (!token) {
return res.status(200).send({
error: true,
msg: "Authentication Failed.",
});
}
jwt.verify(token, process.env.JWT, (err, user) => {
if (err) {
return res.status(200).send({
error: true,
msg: "Authentication Failed.",
});
}
req.user = user;
next();
});
};
route arts.js
import {
ChangeArt
} from "../controller/arts.js";
import express from "express";
import { verifyToken } from "../verifyToken.js";
const router = express.Router();
router.put("/ChangeArt/:id",verifyToken, ChangeArt);
Login.js Frontend
res
.cookie("access_token", token, {
httpOnly: true,
maxAge: 24 * 60 * 60 * 1000,
SameSite: process.env.NODE_ENV === "development" ? false : true,
})
.status(200)
.json(others);
Server
//Middleware
const corsOptions = {
credentials: true,
origin: "http://localhost:3000", //or production URL
};
app.use(cookieParser());
app.use(cors(corsOptions));
So I know there are tons of similar questions out there, and I've read most of them in the past few days. However I didn't find any solution to my problem. The app is about users can post memories(cards) etc... Point is, when I create a new card with POST request, there is no problem, but when I want to sign up a user then all hell breaks loose and throws this error:
(node:2732) UnhandledPromiseRejectionWarning: TypeError: Cannot destructure property 'firstName' of 'req.body' as it is undefined.
at signup (file:///E:/projects/personal/memories-app/backend/controllers/user.controller.js:39:13)
at Layer.handle [as handle_request] (E:\projects\personal\memories-app\backend\node_modules\express\lib\router\layer.js:95:5)
at next (E:\projects\personal\memories-app\backend\node_modules\express\lib\router\route.js:137:13)
I don't know that could be the problem, because other functions work so dunno really.
Here are the codes
server.js
import express from 'express';
import mongoose from 'mongoose';
import cors from 'cors';
import dotenv from 'dotenv';
import postRoutes from './routes/posts.routes.js';
import userRoutes from './routes/users.routes.js';
const app = express();
dotenv.config();
app.use(express.json({ extended: true }));
app.use(express.urlencoded({ extended: true }));
app.use(cors());
app.use('/posts', postRoutes);
app.use('/users', userRoutes);
app.get('/', (req, res) => {
res.send('Hello to Memories API');
});
const PORT = process.env.PORT || 5000;
mongoose
.connect(process.env.CONNECTION_URL, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() =>
app.listen(PORT, () => console.log(`Server running on port: ${PORT}`))
)
.catch((error) => console.log(error.message));
mongoose.set('useFindAndModify', false);
user.model.js
import mongoose from 'mongoose';
const userSchema = mongoose.Schema({
name: { type: String, required: true },
email: { type: String, required: true },
password: { type: String, required: true },
id: { type: String },
});
export default mongoose.model('User', userSchema);
the sign up method from user.controller.js
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import User from '../models/user.js';
export const signup = async (res, req) => {
const { firstName, lastName, email, password, confirmPassword } = req.body;
try {
const existingUser = await User.findOne({ email });
if (existingUser)
return res.status(400).json({ message: 'User already exists' });
if (password !== confirmPassword)
return res.status(400).json({ message: "Passwords don't match" });
const hashedPassword = await bcrypt.hash(password, 12);
const result = await User.create({
email,
password: hashedPassword,
name: `${firstName} ${lastName}`,
});
const token = jwt.sign(
{ email: result.email, id: result._id },
'test',
{
expiresIn: '1h',
}
);
res.status(200).json({ result, token });
} catch (error) {
res.status(500).json({ message: 'Something went wrong.' });
}
};
and just to see the createPost method (which works) from post.controller.js
import PostMessage from '../models/postMessage.js';
import mongoose from 'mongoose';
export const createPost = async (req, res) => {
const post = req.body;
console.log(post);
const newPost = new PostMessage(post);
try {
await newPost.save();
res.status(201).json(newPost);
} catch (error) {
res.status(409).json({ message: error.message });
}
};
And there is no problem with the front-end because when I simply console.log the req, I can see the body, but if I were to clg the req.body, then it is undefined. I've tried it with postman also, but no luck.
I would appreciate any insight on this! Thanks in advance!
You need to swap the order of res and req in the signup function, replace:
export const signup = async (res, req) => {
by:
export const signup = async (req, res) => {
Your User model does not have a firstName, lastName, and confirmPassword types use { name, email, password, } = req.body to sign up a new user.
In your project frontend use
name email, password, confirmPassword to register a use and email, password to log users in.
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`);
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
THE app is suppose to register the new user and send the new users info to the MongoDB, but when i attempt to register the user it throws an error of 500 internal error. the console says the error is in the user file, the terminal say this is the error, Proxy error: Could not proxy request /api/users from localhost:3000 to https://localhost:5000.
[1] See https://nodejs.org/api/errors.html#errors_common_system_errors for more information (EPROTO).
I've already tried changing the proxy in the packet.json by giving it a different path and target but its not working. maybe i'm overlooking something. enter code here
import React, { useReducer } from 'react';
import axios from 'axios';
import AuthContext from './authContext';
import authReducer from './authReducer';
import {
REGISTER_SUCCESS,
REGISTER_FAIL,
USER_LOADED,
AUTH_ERROR,
LOGIN_SUCCESS,
LOGIN_FAIL,
LOGOUT,
CLEAR_ERRORS
} from '../types';
const AuthState = props => {
//initial state
const initialState = {
token: localStorage.getItem('token'),
isAuthenticated: null,
user: null,
loading: true,
error: null
};
const [ state, dispatch ] = useReducer(authReducer, initialState);
// load user
const loadUser = () => console.log('load user') ;
// register user
const register = async formData => {
const config = {
headers: {
'Content-Type': 'application/json'
}
}
try {
const res = await axios.post('api/users', formData, config);
dispatch({
type: REGISTER_SUCCESS,
payload: res.data
});
} catch (err){
dispatch({
type: REGISTER_FAIL,
payload: err.response.data.msg
});
}
}
// login user
const login = () => console.log('login') ;
//logut
const logout = () => console.log('logout') ;
// clear errors
const clearErrors = () => console.log('clearErrors') ;
return (
<AuthContext.Provider
value= {{
token: state.token,
isAuthenticated: state.isAuthenticated,
loading: state.loading,
user: state.user,
error: state.error,
register,
loadUser,
login,
logout,
clearErrors
}}>
{props.children}
</AuthContext.Provider>
);
};
export default AuthState;
//this is my server.js file with the routes
const express = require('express');
const connectDB = require('./config/db')
//connect MongoDB
connectDB();
const app = express();
//init middleware
app.use(express.json({extended: false}));
app.get('/', (req, res) => res.json({ msg: 'hello welcome'})
);
//define routes
app.use('/api/users', require('./routes/users'));
app.use('/api/auth', require('./routes/auth'));
app.use('/api/contacts', require('./routes/contacts'))
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`server is working on ${PORT}`))
// this is mongoDB code
const mongoose = require('mongoose');
const config = require('config');
const db = config.get('mongoURI');
const connectDB = async () =>{
try{ await
mongoose.connect(db, {
useNewUrlParser: true,
useCreateIndex: true,
useFindAndModify: false
});
console.log('mongo connected..')
} catch (err){
console.log(err.message);
process.exit(1)
}
};
module.exports = connectDB;
// this the users file where the console is throwing the 500 internal error.
const express = require('express');
const router = express.Router();
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const config = require('config');
const { check, validationResult } = require('express-validator');
const User = require('../models/User')
// This route Post request to api/users,
// description register a user,
// access to public to register an become a user
router.post('/', [
check('name', 'Name is require').not().isEmpty(),
check('email', 'please include email').isEmail(),
check('password', 'enter a password with atleast 6 characters'
).isLength({min: 6})
],
async (req, res) =>{
const errors = validationResult(req);
if(!errors.isEmpty()){
return res.status(400).json({ errors: errors.array()});
}
const { name, email, password } = req.body;
try{
let user = await User.findOne({email});
if(user){
return res.status(400).json({msg: 'user already exist'})
}
user = new User({
name,
email,
password
});
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(password, salt);
await user.save();
// object to send in the token
const payload = {
user: {
id: user.id
}
}
jwt.sign(payload, config.get('jwtSecret'), {
expiresIn: 36000
}, (err, token) => {
if(err) throw err;
res.json({token});
});
} catch (err){
console.log(err.message);
res.status(500).send('server error')
}
});
module.exports = router;
I figure out the problem!!!
I had an unexpected token in my users file that simple colon was interfering with the code