I try to learn REST API and session based Authenticaion in express.js. But I got really interesting error when try to relocate the endpoints.
After relocating the endpoints I send a request to /me endpoint but I get an error. For instance;
// This code works fine
router.get("/me", sessionChecker, async (req, res, next) => {
const { userId } = req.session.payload;
const user = await UserService.findUserById(userId);
return res.json(user);
});
router.get("/:userId", sessionChecker, async (req, res, next) => {
const { userId } = req.params;
const user = await UserService.findUserById(userId);
return res.json(user);
});
to this;
// This code gives error
router.get("/:userId", sessionChecker, async (req, res, next) => {
const { userId } = req.params;
const user = await UserService.findUserById(userId);
return res.json(user);
});
router.get("/me", sessionChecker, async (req, res, next) => {
const { userId } = req.session.payload;
const user = await UserService.findUserById(userId);
return res.json(user);
});
I'am getting this error;
/Users/Desktop/projects/Curioso/backend/node_modules/mongoose/lib/query.js:4913
const castError = new CastError();
^
CastError: Cast to ObjectId failed for value "me" (type string) at path "_id" for model "User"
at model.Query.exec (/Users/Desktop/projects/Curioso/backend/node_modules/mongoose/lib/query.js:4913:21)
at model.Query.Query.then (/Users/Desktop/projects/Curioso/backend/node_modules/mongoose/lib/query.js:5012:15)
at processTicksAndRejections (node:internal/process/task_queues:96:5) {
messageFormat: undefined,
stringValue: '"me"',
kind: 'ObjectId',
value: 'me',
path: '_id',
reason: BSONTypeError: Argument passed in must be a string of 12 bytes or a string of 24 hex characters or an integer
I couldn't understand what is the problem or logic of this error. Here is the rest of the code;
index.js
const express = require("express");
const session = require("express-session");
const MongoStore = require("connect-mongo");
const mongoose = require("mongoose");
const authRouter = require("./routes/auth");
const roomsRouter = require("./routes/rooms");
const usersRouter = require("./routes/users");
var cors = require("cors");
require("dot-env");
const app = express();
mongoose
.connect(process.env.MONGODB_URL)
.then(() => {
console.log("Connected to DB");
})
.catch((error) => {
console.log(error);
});
var whitelist = ["http://localhost:3000"];
var corsOptions = {
origin: whitelist,
methods: ["POST", "PUT", "GET", "OPTIONS", "HEAD"],
credentials: true,
};
app.use(cors(corsOptions));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(
session({
secret: process.env.SESSION_SECRET_KEY,
resave: false,
saveUninitialized: true,
cookie: {
maxAge: 1000 * 60 * 60 * 24,
secure: process.env.NODE_ENV === "production",
httpOnly: true,
},
store: MongoStore.create({
mongoUrl: process.env.MONGODB_URL,
}),
})
);
app.use("/auth", authRouter);
app.use("/rooms", roomsRouter);
app.use("/users", usersRouter);
app.listen(8000, () => {
console.log(`Example app listening on port 8000`);
});
routes/user.js
const express = require("express");
const { sessionChecker } = require("../middlewares/auth");
const router = express.Router();
const UserService = require("../services/user");
router.get("/", sessionChecker, async (req, res, next) => {
const allUsers = await UserService.getAllUsers();
return res.json(allUsers);
});
router.get("/:userId", sessionChecker, async (req, res, next) => {
const { userId } = req.params;
const user = await UserService.findUserById(userId);
return res.json(user);
});
router.get("/me", sessionChecker, async (req, res, next) => {
const { userId } = req.session.payload;
const user = await UserService.findUserById(userId);
return res.json(user);
});
module.exports = router;
middlewares/auth.js
const { HTTP_ERRORS } = require("../utils/constants");
const sessionChecker = (req, res, next) => {
const userSession = req.session.payload.userId;
if (!userSession) {
return res
.status(HTTP_ERRORS.UNAUTHORIZED.CODE)
.send(HTTP_ERRORS.UNAUTHORIZED.MESSAGE);
}
next();
};
module.exports = { sessionChecker };
Because you are using pattern matching /me will go to the route /:userId. Express follows routes from first defined to last defined to find a matching route, this is why the order matters.
It is the practise to put the pattern matching as the last route so /:userId should be the last route.
Related
I'm trying to authenticate users locally using the passport-jwt strategy or with their google account using passport-google-oauth20 strategy. The local authentication with passport-jwt works fine but when I try to access the protected route after google signup, req.isAuthenticated() keeps returning false.
Here's what my code looks like
passport-jwt config
const passport = require("passport");
const JwtStrategy = require("passport-jwt").Strategy;
const ExtractJwt = require("passport-jwt").ExtractJwt;
const User = require("../models/User");
const opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = process.env.JWT_SECRET;
passport.use(
new JwtStrategy(opts, (jwt_payload, done) => {
User.findOne({ id: jwt_payload.id }, function (err, user) {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
}
});
})
);
passport-google-oauth20 config
const passport = require("passport");
const GoogleStrategy = require("passport-google-oauth20").Strategy;
const User = require("../models/User");
passport.use(
new GoogleStrategy(
{
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: "http://localhost:5000/auth/google/protected",
},
(accessToken, refreshToken, profile, cb) => {
// console.log(profile);
const userInfo = {
googleId: profile.id,
fullname: profile.displayName,
email: profile.emails[0].value,
};
User.findOrCreate(userInfo, (err, user) => {
return cb(err, user);
});
}
)
);
controllers/auth.js
const User = require("../models/User");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const createError = require("../utils/error");
const jsonToken = require("../utils/verifyToken");
const passport = require("passport");
module.exports = {
googleAuth: passport.authenticate("google", {
session: false,
scope: ["email", "profile"],
}),
googleAuthRedirect: passport.authenticate("google", {
session: false,
failureRedirect: "auth/login",
}),
authRedirectCallBack: (req, res) => {
// const token = jsonToken.createToken({
// id: req.user._id,
// isAdmin: req.user.isAdnmin,
// services: req.user.services,
// });
// req.headers["Authorization"] = "Bearer " + token;
// console.log(req.headers["Authorization"]);
console.log(req.isAuthenticated()); //this line returns true
res.redirect("/api/protected");
},
};
routes/auth.js
const express = require("express");
const router = express.Router();
const controllers = require("../controllers/auth");
const {
googleAuth,
googleAuthRedirect,
authRedirectCallBack,
} = controllers;
router.route("/google").get(googleAuth);
router.route("/google/protected").get(googleAuthRedirect, authRedirectCallBack);
module.exports = router;
routes/protected.js
const router = require("express").Router();
const passport = require("passport");
router.get(
"/protected",
(req, res, next) => {
console.log("protected route returns: " + req.isAuthenticated()); //this line returns false
if (req.isAuthenticated()) {
res.send("this is the protected route");
} else {
return res.send("you're not authorized");
}
next();
},
passport.authenticate("jwt", { session: false }),
(req, res) => {
res.send("this is the protected route");
}
);
module.exports = router;
finally, server.js
const express = require("express");
const app = express();
const dotenv = require("dotenv");
dotenv.config();
const mongoose = require("mongoose");
const passport = require("passport");
require("./config/passportJwt");
require("./config/passportGoogle");
mongoose.connect("mongodb://localhost:27017/findersDB", {
useUnifiedTopology: true,
useNewUrlParser: true,
});
mongoose.connection.on("connected", () => console.log("DB connected!"));
app.use(express.json());
app.use(passport.initialize());
const authRoute = require("./routes/auth");
const protectedRoute = require("./routes/protected");
app.use("/auth", authRoute);
app.use("/api", protectedRoute);
app.use((err, req, res, next) => {
const errorMessage = err.message || "Something went wrong";
const errorStatus = err.status || 500;
return res.status(errorStatus).json({
success: false,
message: errorMessage,
status: errorStatus,
stack: err.stack,
});
});
app.listen(5000, () => console.log("Server is running on port 5000"));
I've tried every solution I've seen so far but none seems to work. Any help will be very much appreciated.
I have a problem with my controller when I'm writing console.log(req); I have all the content of the request body but when I write console.log(req.body); is undefined. I'm trying to write my Portfolio with Next.js React and Express.
This is my server index.js:
const express = require('express');
const next = require('next');
const routes = require('../routes');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
// SERVICE
const authService = require('./services/auth');
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = routes.getRequestHandler(app);
const config = require('./config');
const portfolioRoutes = require('./routes/portfolio');
const secretData = [
{ id: '1',
title: 'Secret Data',
description: 'plans for build something !'
},
{
id: '2',
title: 'Secret Data2',
description: 'plans for build something2 !'
}
]
//MONGODB
mongoose.connect(config.DB_URI, {useNewUrlParser: true, useUnifiedTopology: true})
.then(() => {
console.log("Db connected");
}).catch(err => console.log(err));
app.prepare()
.then(() => {
const server = express();
server.use(bodyParser.json());
server.use(bodyParser.urlencoded({ extended: false }));
var jsonParser = bodyParser.json()
server.use('/api/v1/portfolio', portfolioRoutes);
server.get('/api/v1/secret', authService.checkJWT, (req,res) => {
return res.json(secretData);
})
server.get('/api/v1/ownersite', authService.checkJWT, authService.checkRole('siteOwner'),
(req,res) => {
return res.json(secretData);
})
server.get('*', jsonParser,(req,res) => {
return handle(req,res);
})
server.use(function (err, req, res, next){
if (err.name === 'UnauthorizedError') {
res.status(401).send({title: `Invalid token...`});
}
});
server.use(handle).listen(3000, (err) => {
if(err) throw err
console.log('> Ready on http://localhost:3000');
})
}).catch((ex) => {
console.error(ex.stack)
process.exit(1);
})
This is my routes :
const express = require('express');
const router = express.Router();
const portfolioCtrl = require('../controllers/portfolio');
const authService = require('../services/auth');
router.route('').get(authService.checkJWT, authService.checkRole('siteOwner'),
portfolioCtrl.getPortfolio);
router.route('').post(authService.checkJWT, authService.checkRole('siteOwner'),
portfolioCtrl.savePortfolio);
router.route('/:id').patch(authService.checkJWT, authService.checkRole('siteOwner'),
portfolioCtrl.updatePortfolio);
router.route('/:id').delete(authService.checkJWT, authService.checkRole('siteOwner'),
portfolioCtrl.deletePortfolio);
module.exports = router;
This is my Controller:
savePortfolio: (res, req) => {
console.log(req);
const portfolioData = req.body;
const portfolio = new Portfolio(portfolioData);
portfolio.save((err, createdPortfolio) => {
if(err) {
return res.status(422).send(err);
}
return res.json(createdPortfolio);
})
},
Express route's callback function takes the parameters in the following order:
(req, res, next) =>{...}
req, the request object.
res, the response object.
next, indicating the next middleware function (Optional)
savePortfolio: (res, req) => {...} has the order wrong. That is why req.body would be undefined.
Correction: savePortfolio: (req, res) => {...}
I am trying to post to my cosmosDB using Angular. I can GET just fine, but POST returns with a 404 error in Postman. I am new to routes and APIs so I am a little lost on what is causing the issue.
Here is my index.js
const bodyParser = require('body-parser');
const path = require('path');
const routes = require('./routes');
const root = './';
const port = process.env.PORT || '3000';
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(root, 'dist/checkin')));
app.use('/api', routes);
app.get('*', (req, res) => {
res.sendFile('dist/checkin/index.html', {root});
});
app.listen(port, () => console.log(`API running on localhost:${port}`));
My routes.js
const contactService = require('./contact.service');
const router = express.Router();
router.get('/contacts', (req, res) => {
contactService.getContacts(req, res);
});
router.post('/contact/', (req, res) => {
contactService.postContact(req, res);
});
module.exports=router;
My contact.service.js which contains all of my operations (Just GET and POST right now)
const ReadPreference = require('mongodb').ReadPreference;
require('./mongo').connect();
function getContacts(req, res) {
const docquery = Contact.find({}).read(ReadPreference.NEAREST);
docquery
.exec()
.then(contacts => {
res.status(200).json(contacts);
})
.catch(error => {
res.status(500).send(error);
return;
});
}
function postContact(req, res) {
const originalContact = { uid: req.body.uid, name: req.body.name, description: req.body.description };
const contact = new Contact(originalContact);
contact.save(error => {
if (checkServerError(res, error)) return;
res.status(201).json(contact);
console.log('Contact created successfully!');
});
}
function checkServerError(res, error) {
if (error) {
res.status(500).send(error);
return error;
}
}
module.exports = {
getContacts,
postContact
};
Input is obtained through an HTML forum which is picked up and sent through
return this.http.post<Contact>(`${api}/contact/`, contact);
}
I'm trying Nextjs for the first time and I added express to use mongodb. I got so far as getting login in with my google credentials but I'm struggling getting the session info to my user's profile page.
Server.js:
const express = require('express');
const next = require('next');
const bodyParser = require("body-parser");
const mongoose = require("mongoose");
mongoose.Promise = global.Promise;
const passport = require("passport");
const LocalStrategy = require("passport-local");
const exsession = require("express-session");
const User = require('../models/User');
const port = process.env.PORT || 8080;
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
const middleware = require("../middleware/middleware")
const uri = "XXXX"
app.prepare()
.then(() => {
const server = express();
const showRoutes = require('./routes/index');
const authRouter = require('./routes/auth');
mongoose.connect(uri)
.then(function () {
console.log('Connected to MONGOD !!');
}).catch(function (err) {
console.log('Failed to establish connection with MONGOD !!');
console.log(err.message);
});
server.use(express.static(__dirname + "/public"));
server.use(bodyParser.urlencoded({extended: true}));
server.use(bodyParser.json());
/////////////////////////////////////
//passport configuration
/////////////////////////////////////
server.use(exsession({
secret: "projectx",
resave: false,
saveUninitialized: false,
}));
server.use(passport.initialize());
server.use(passport.session());
passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
server.use(function(req, res, next){
res.locals.currentUser = req.user;
next();
})
server.use('/api', showRoutes);
server.use('/auth', authRouter);
server.get('/login', (req, res) => {
return app.render(req, res, '/login', req.query )
})
server.get('/profile', middleware.isLoggedIn, (req, res, next) => {
console.log(req.query)
return app.render(req, res, '/profile', { user: req.user } )
})
server.get('/post/:id', (req, res) => {
return app.render(req, res, '/post', { id: req.params.id })
})
server.get("*", (req, res) => {
return handle(req,res);
})
server.listen(port, err => {
if (err) throw err;
console.log(`> Ready on http://localhost:${port}`)
})
})
.catch(ex => {
console.log(ex.stack);
process.exit(1);
})
Profile.js:
import React, { Component } from 'react'
export default class extends Component {
static getInitialProps ({ user: { user } }) {
console.log(user)
return { user: user }
}
render () {
return <div>
<h1>Welcome{this.props.user}</h1>
</div>
}
}
I'm trying to display my current user's name in my profile page however i get undefined in my console.
my studies/add wont render. I get "CastError: Cast to ObjectId failed for value "add" at path "_id" " error. I just dont get it, nothing seems to work the way I would expect. Im quite new to express. I tried all kind of different things but it just wont render /studies/add
my studies route `
const express = require('express')
const router = express.Router()
const Studies = require('../models/studies')
router.get('/', (req, res) => {
Studies.find({}, (err, studies) => {
studies.sort(function (a, b) {
return new Date(b.endDate) - new Date(a.endDate)
})
if (err) {
console.log(err)
} else {
res.render('studies', {
studies
})
}
})
})
router.get('/:id', function (req, res) {
Studies.findById(req.params.id, function (err, studies) {
if (err) {
console.log(err)
} else {
res.render('course', {
studies
})
}
})
})
router.get('/add', function (err, req, res) {
if (err) {
console.log(err)
}
res.render('addstudy')
})
module.exports = router
`
my app.js file
const express = require('express')
const bodyParser = require('body-parser')
const path = require('path')
const config = require('./config/database')
const mongoose = require('mongoose')
const Studies = require('./models/studies')
const session = require('express-session')
const passport = require('passport')
// const flash = require('connect-flash')
const app = express()
mongoose.connect(config.database)
let db = mongoose.connection
// Check connection
db.once('open', function () {
console.log('Connected to MongoDB')
})
// Check for DB errors
db.on('error', function (err) {
console.log(err)
})
const logger = function (req, res, next) {
console.log('loogging..')
next()
}
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'ejs')
// Body Parser Middleware
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())
app.use(logger)
// Passport Config
require('./config/passport')(passport)
// Passport Middleware
app.use(passport.initialize())
app.use(passport.session())
// Express Session Middleware
app.use(session({
secret: 'keyboard cat',
resave: true,
saveUninitialized: true
}))
// Express Messages Middleware
/* app.use(require('connect-flash')())
app.use(function (req, res, next) {
res.locals.messages = require('express-messages')(req, res)
next()
}) */
app.use(express.static('public'))
app.get('*', function (req, res, next) {
res.locals.user = req.user || null
next()
})
app.get('/', (req, res) => {
Studies.find({}, (err, studies) => {
console.log(studies)
if (err) {
console.log(err)
} else {
res.render('index', {
name: studies[0].name,
description: studies[0].description
})
}
})
})
let studies = require('./routes/studies')
let work = require('./routes/work')
let about = require('./routes/about')
let users = require('./routes/users')
app.use('/studies', studies)
app.use('/work', work)
app.use('/about', about)
app.use('/users', users)
app.listen(3002, () => {
console.log('started on 3002')
})