Been trying to get user user object by id from parameters, using User.findById and cannot access this route.
Postman response: Cannot GET /users/get/ with status 404 (not found) which is wierd.
Postman JSON response:
Unexpected '<'
Been using express-promise-router but also tried with default express.Router();
Here is my code:
routes/users.js
const express = require('express');
const router = require('express-promise-router')();
const passport = require('passport');
const router1 = express.Router();
require('../passport');
const { validateBody, schemas } = require('../helpers/routeHelpers');
const UsersController = require('../controllers/users');
const passportSignIn = passport.authenticate('local', { session: false });
const passportJWT = passport.authenticate('jwt', { session: false });
router.route('/signup')
.post(validateBody(schemas.authSchema), UsersController.signUp);
router.route('/signin')
.post(validateBody(schemas.authSchema), passportSignIn, UsersController.signIn);
router.route('/get/:id')
.get(UsersController.getUser);
router.route('/secret')
.get(passportJWT, UsersController.secret);
controllers/users
module.exports = router;
const JWT = require('jsonwebtoken');
const User = require('../models/user');
const { JWT_SECRET } = require('../configuration');
const signToken = (user) => {
return JWT.sign({
iss: 'CodeWorkr',
sub: user.id,
iat: new Date().getTime(), // current time
exp: new Date().setDate(new Date().getDate() + 1) // current time + 1 day ahead
}, JWT_SECRET);
};
module.exports = {
signUp: async (req, res) => {
const { email, password } = req.value.body;
// Check if there is a user with the same email
const foundUser = await User.findOne({ 'local.email': email });
if (foundUser) {
return res.status(403).json({ error: 'Email is already in use' });
}
// Create a new user
const newUser = new User({
method: 'local',
local: {
email: email,
password: password
}
});
await newUser.save();
// Generate the token
const token = signToken(newUser);
// Respond with token
return res.status(200).json({ token });
},
signIn: async (req, res) => {
// Generate token
const token = signToken(req.user);
res.status(200).json({ token });
},
getUser: async (req, res) => {
User.findById(req.params.id)
.then((user) => {
res.status(200).json({ user });
console.log('test');
});
},
secret: async (req, res) => {
console.log('I managed to get here!');
res.json({ secret: 'resource' });
}
};
server.js
const express = require('express');
const morgan = require('morgan');
const bodyParser = require('body-parser');
const cors = require('cors');
const mongoose = require('mongoose');
const passport = require('passport');
const db = require('./configuration/config').mongoURI;
const dbTest = require('./configuration/config').mongoURITest;
mongoose.Promise = global.Promise;
if (process.env.NODE_ENV === 'test') {
mongoose.connect(dbTest, { useMongoClient: true });
} else {
mongoose.connect(db, { useMongoClient: true });
}
const app = express();
app.use(cors());
app.use(passport.initialize());
app.use(passport.session());
// Middlewares moved morgan into if for clear tests
if (!process.env.NODE_ENV === 'test') {
app.use(morgan('dev'));
}
app.use(bodyParser.json());
// Routes
app.use('/users', require('./routes/users'));
// Start the server
const port = process.env.PORT || 3001;
app.listen(port);
console.log(`Server listening at ${port}`);
Other post routes works fine,
Related
When i tried to use it with react app i can not reach the value. I use sequelize orm and mysql database. I want to send the session information to the react side when the user logs in
app.js file:
const express = require('express')
const dotenv = require("dotenv")
const app = express()
const body_parser = require("body-parser");
const cookieParser = require("cookie-parser")
const session = require('express-session')
const cors = require("cors")
dotenv.config()
app.use(cors())
const SequelizeStore = require("connect-session-sequelize")(session.Store);
const adminRoute = require("./routes/admin")
const locals = require("./middlewares/locals")
app.use(cookieParser())
app.use(express.urlencoded({ extended: false }))
app.use(body_parser.json())
const sequelize = require("./data/db")
app.use(session({
secret: "hello world",
resave: false,
saveUninitialized: false,
cookie: {
maxAge: 1000 * 60 * 60 * 24
},
store: new SequelizeStore({
db: sequelize
})
}))
app.use(locals);
app.use(adminRoute)
const dummy = require("./data/dummy-data")
async function x() {
// await sequelize.sync({ force: true })
// await dummy()
}
x()
app.listen(process.env.PORT, () => {
console.log(`Server running on Port: ${process.env.PORT}`)
})
locals.js file:
module.exports = (req, res, next) => {
res.locals.auth = req.session.auth
console.log("auth : ", res.locals.auth)
next();
}
admin.js routes:
router.get("/api/session", async (req, res) => {
res.json({ isAuth: res.locals.isAuth });
});
router.post("/login", async (req, res) => {
const { email, password } = req.body
console.log(req.body)
try {
const user = await Users.findOne({
where: {
email: email
}
})
if (!user) {
return res.status(404)
}
const match = await bcrypt.compare(password, user.password)
console.log("match:", match)
if (match) {
req.session.isAuth = 1
return res.status(200).send({ message: "login successful" })
}
else {
req.session.isAuth = 0
return res.status(404)
}
}
catch (err) {
console.log(err)
}
})
React Login.js:
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const login = async (e) => {
e.preventDefault()
const response = await axios.post("http://localhost:5000/login", { email: email, password: password })
console.log(response)
if(response.status === 200){
const response2 = await axios.get("http://localhost:5000/api/session")
console.log(response2)
}
}
Login.js first response = data: {message: 'login successful'}
Login.js second response = data: {}
If i try to react http://localhost:5000/login by hand on the browser auth sets as true. But when i refresh react side it sets undefined again.
Using thunderclient (similar to postman) i cant access this employee api which requires a jwt, even though i confirm i am already authorized. Here is my code:
authController:
const usersDB = {
users: require("../model/users.json"),
setUsers: function (data) {
this.users = data;
},
};
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
require("dotenv").config();
const fsPromises = require("fs").promises;
const path = require("path");
const handleLogin = async (req, res) => {
const { user, pwd } = req.body;
if (!user || !pwd)
return res
.status(400)
.json({ message: "Username and password are required." });
const foundUser = usersDB.users.find((person) => person.username === user);
console.log(foundUser);
if (!foundUser) return res.sendStatus(401);
const match = await bcrypt.compare(pwd, foundUser.password);
if (match) {
const accessToken = jwt.sign(
{ username: foundUser.username },
process.env.ACCESS_TOKEN_SECRET,
{ expiresIn: "60s" }
);
const refreshToken = jwt.sign(
{ username: foundUser.username },
// we need our secret from env file as well to make our jwt
process.env.REFRESH_TOKEN_SECRET,
{ expiresIn: "1d" }
);
const otherUsers = usersDB.users.filter(
(person) => person.username !== foundUser.username
);
const currentUser = { ...foundUser, refreshToken };
usersDB.setUsers([...otherUsers, currentUser]);
await fsPromises.writeFile(
path.join(__dirname, "..", "model", "users.json"),
JSON.stringify(usersDB.users)
);
res.cookie("jwt", refreshToken, {
httpOnly: true,
ameSite: "None",
secure: true,
maxAge: 24 * 60 * 60 * 1000,
});
res.json({ accessToken });
} else {
res.sendStatus(401);
}
};
module.exports = { handleLogin };
sever.js:
const express = require("express");
const app = express();
const path = require("path");
const cors = require("cors");
const corsOptions = require("./config/corsOptions");
const { logger } = require("./middleware/logEvents");
const errorHandler = require("./middleware/errorHandler");
const cookieParser = require("cookie-parser");
const verifyJWT = require("./middleware/verifyJWT");
const PORT = process.env.PORT || 3500;
app.use(logger);
app.use(cors(corsOptions));
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
app.use(cookieParser());
app.use(express.static(path.join(__dirname, "./public")));
// routes
app.use("/", require("./routes/root"));
app.use("/register", require("./routes/register"));
app.use("/auth", require("./routes/auth"));
app.use("/refresh", require("./routes/refresh"));
app.use(verifyJWT);
app.use("/employees", require("./routes/api/employees"));
app.all("/*", (req, res) => {
res.status(404);
if (req.accepts("html")) {
res.sendFile(path.join(__dirname, "views", "404.html"));
} else if (req.accepts("json")) {
res.json({ error: "404 Not Found" });
} else {
res.type("txt").send("404 not found");
}
});
app.use(errorHandler);
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
verifyJWT middleware:
const jwt = require("jsonwebtoken");
require("dotenv").config();
const verifyJWT = (req, res, next) => {
const authHeader = req.headers["authorization"];
if (!authHeader) return res.sendStatus(401);
console.log(authHeader);
// bearer token, hence bearer space 1, 1 is the token
const token = authHeader.split("")[1];
// decoded info from the jwt
jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err, decoded) => {
// 403 is forbidden
if (err) return res.sendStatus(403);
req.user = decoded.username;
next();
});
};
module.exports = verifyJWT;
so if i for example
http://localhost:3500/auth (post) and login with a user and pwd, my res does log an access token, and if i try to use that inside
http://localhost:3500/employees (get) i get forbidden. not sure what i am missing here
i tried console.logging to see if i had foundUser, which i did, so not sure why i cant get into this route
You are splitting by empty string, it would divide every character, try spliting by space:
const token = authHeader.split(" ")[1];
I have my Node/Express server running. I have the main server.js file where most of the code is for the server. Now I want to separate out the routes into a separate file. I have done this before using app.use(routes). But the problem is, I want to pass a string in for one of the routes to use.
Here is my server.js code:
// other imports
import routes from './routes.js';
const app = express();
...
const port = Number.parseInt(process.env.PORT, 10) || 3001;
const serverType = process.env.NODE_ENV === 'production' ? 'Production' : 'Test';
const statusMsg = `${serverType} Node server for external facing web server on ${port}`;
// i want `routes` to have access to `statusMsg`
app.use(routes);
Then in routes.js:
import express from 'express';
const router = express.Router();
router.get('/', (req, res) => res.status(200).send(statusMsg);
export default router;
I use serverType and port elsewhere in server.js, else I would just move all that code to routes.js.
Update
Adding in updated routes.js as I understand it with suggestion from jonrsharpe.
import express from 'express';
const router = express.Router();
const createRoutes = (statusMsg) => {
router.get('/', (req, res) => res.status(200).send(statusMsg);
};
export default createRoutes;
You can separate the server logic, routes logic, and business logic (usually inside a separate file called a controller).
inside the server file try blow code:
const express = require('express');
const app = express();
const bodyParser = require('body-parser')
require('dotenv').config();
const connectDB = require('./config/config')
const cookieParser = require('cookie-parser')
const authRoutes = require('./routes/authRoutes')
const categoryRoutes = require('./routes/categoryRoutes')
const cors = require('cors');
connectDB();
app.use(bodyParser.json());
app.use(cookieParser());
app.use(cors());
//Route Mounting
app.use('/', authRoutes);
app.use('/', categoryRoutes);
app.listen(process.env.PORT, ()=>{
console.log(`Server is running on PORT: ${process.env.PORT}`)
})
Then create a separate file authRoute.js and do the following code
const express = require('express')
const { registerUser, loginUser, getAllUsers, logoutUser} = require('../controllers/authController')
const router = express.Router()
const {isAuthenticatedUser, isAuthorizedRoles} = require('../middleware/auth')
router.route('/user/new').post(registerUser);
router.route('/user/login').post(loginUser);
router.route('/users').get(getAllUsers);
router.route('/account/logout').get(logoutUser);
module.exports = router;
lastly to write the business logic create file authController.js and place the following code.
const User = require('../model/userSchema');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
require('dotenv').config();
exports.registerUser = async (req, re, next)=>{
let password = req.body.password;
password = await bcrypt.hash(password, 10);
const newUser = req.body;
newUser.password = password;
try {
const user = await User.create(newUser);
if(user){
res.json({
success:true,
user
});
}
} catch (error) {
console.log(error);
}
}
exports.loginUser = async function(req, res){
const email = req.body.email;
const password = req.body.password;
if(!email || !password){
return res.json({
success:false,
message:'Please provide the email & Password'
})
}
const user = await User.findOne({email:email});
if(!user){
return res.json({
success:false,
message:'user with this email not found in database'
})
}
const isPasswordMatch = await bcrypt.compare(password, user.password);
if(!isPasswordMatch){
return res.json({
success:false,
message:'Your password is wrong...'
})
}
const token = jwt.sign({ _id: user._id }, process.env.JWT_SECRET, { expiresIn: process.env.JWT_EXPIRY_TIME });
res.cookie('token', token, {httpOnly:true, expires:new Date(Date.now() + 60*60*1000 )}).json({
success:true,
message:'You are logged in! Enjoy',
})
}
exports.getAllUsers = async (req, res, next)=>{
res.json({
success:true,
data:[
{
"id":1,
"name":"Yasir",
"qual":"MCS",
"age":32
},
{
"id":2,
"name":"Tabish",
"qual":"BS",
"age":21
},
{
"id":3,
"name":"Ahmed",
"qual":"BSCS",
"age":32
},
{
"id":4,
"name":"Hassan",
"qual":"MCS",
"age":33
}
]
})
}
exports.logoutUser = async (req, res, next)=>{
res.cookie('token', null, {expires:new Date(Date.now())}).json({
success:true,
message:'You are loggedOut',
})
}
This is the way you can have separation of concerns.
I am trying to create a register page. when I route its shows Cannot GET /api/auth/register same thing happened when I route login and others too. i use postman to throw data it's shown 5000 internet server error.
how to solve route problem for register
// tested REGISTER i used it to test this code is runing or not in tutorial this code was runnign and show in register path is "ok"
// tested REGISTER
const express = require("express");
const router = express.Router();
const User = require("../models/User");
router.get("/register", async (req, res) => {
//create new user
const user = await new User({
username:"admin",
email:"admin#gmail.com",
password: "123456"
});
//save user and respond
await user.save();
res.send("Hello")
});
module.exports = router;
index.js
const express = require("express");
const app = express();
const mongoose = require("mongoose");
const dotenv = require("dotenv");
const helmet = require("helmet");
const morgan = require("morgan");
const userRoute = require("./routes/users");
const authRoute = require("./routes/auth");
const postRoute = require("./routes/posts");
dotenv.config();
mongoose.connect(
process.env.MONGO_URL,
{ useNewUrlParser: true,
useUnifiedTopology: true
},
() => {
console.log("Connected to MongoDB");
}
);
//middleware
app.use(express.json());
app.use(helmet());
app.use(morgan("common"));
app.use("/api/auth", authRoute);
app.use("/api/users", userRoute);
app.use("/api/posts", postRoute);
app.listen(8000, () => {
console.log("Backend server is running!");
});
auth.js here the login and register file
const express = require("express");
const router = express.Router();
const User = require("../models/User");
const bcrypt = require("bcrypt");
//REGISTER
router.post("/register", async (req, res) => {
try {
//generate new password
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(req.body.password,salt);
//create new user
const newUser = new User({
username: req.body.username,
email: req.body.email,
password: hashedPassword,
});
//save user and respond
const user = await newUser.save();
res.status(200).json(user);
} catch (err) {
res.status(500).json(err)
}
});
//LOGIN
router.post("/login", async (req, res) => {
try {
const user = await User.findOne({ email: req.body.email });
// make sure user is not empty {} or [];
if (!user) res.status(404).json("user not found");
const validPassword = await bcrypt.compare(req.body.password,
user.password)
// make sure password is not empty {} or [];
if(!validPassword) res.status(400).json("wrong password");
res.status(200).json(user)
} catch (err) {
res.status(500).json(err)
}
});
module.exports = router;
I am trying to hit http://localhost:5000/api/auth/ with post request using postman but it says "cannot post api/auth".
I am using mongo db and node js server.js
server.js file:
const express = require('express');
var connectDB = require('./config/db');
const app = express();
//connect database
connectDB();
// initializing middleware
app.use(express.json({extended: false}));
app.get('/', (req,res) =>
res.json({msg:'Hellow we are here'}));
//Define Routes
app.use('api/auth', require('./routes/auth'));
app.use('/api/users', require('./routes/users'));
app.use('/api/contacts', require('./routes/contacts'));
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log ("server running at port 5000"));
Here is the auth file:
const express = require('express');
const router = express.Router();
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const {check, validationResult} = require('express-validator');
const config = require('config');
const User = require('../models/User');
//#route GET api/auth
//#desc Get logged in user
//#access private
router.get('/', (req, res)=> {
res.send('Get logged in suser');
});
//#route POST api/auth
//#desc Auth user and get token
//#access public
router.post (
'/',
[
check('email', 'Please enter a valid email').isEmail(),
check('password', 'password required').exists(),
],
async (req, res)=> {
const errors = validationResult(req);
if(!errors.isEmpty()){
return res.status(400).json({errors: errors.array()});
}
const {email , password} = req.body;
try{
let user= await User.findOne({email});
console.log(user);
if(!user)
{
return res.status(400).json({msg:"Invalid credentials"});
}
const isMatch = await bcrypt.compare(password, user.password);
if(!isMatch)
{
return res.status(400).json({msg:"Invalid credentials"});
}
const payload = {
user: { id: user.id }
};
jwt.sign(payload,
config.get('jwtSecret'),
{expiresIn: 36000},
(err, token) => {
if (err)
{ throw err;}
res.send({token});
});
}
catch (err) {
console.error(err.message);
res.status(500).send('server error');
}
});
module.exports = router;
There's a missing leading slash where you define the routes. It is
//Define Routes
app.use('api/auth', require('./routes/auth'));
but it should be
//Define Routes
app.use('/api/auth', require('./routes/auth'));