I don't to put all by main routes in my server.js file, so the main file of my express server, because they are too many. Now I created a subroute named auth where the login, register, emailverification and passwordreset are in.
I created two functions to check if the user is logged in or not and now I need to check that the user is not logged in when registering or logging in, but the emailverification he needs to. Therefor, I can't set it as a middle ware in the app.use() function.
server.js:
const userRoute = require("./routes/user");
const authRoute = require("./routes/auth");
const app = express();
(async () => await connection())();
app.use("/auth", authRoute);
async function checkAuthenticated(req, res, next) {
const auth = await checkJWT(req.cookies.token);
if (auth === 500) return res.status(500).send();
if (!auth) return res.status(401).send();
req.user = auth;
return next();
}
async function checkNotAuthenticated(req, res, next) {
const auth = await checkJWT(req.cookies.token);
if (auth === 500) return res.status(500).send();
if (auth) return res.status(401).send();
return next();
}
async function checkJWT(token) {
try {
...
return user;
} catch (err) {
console.log(err);
return 500;
}
}
app.use("/user", checkAuthenticated, userRoute);
app.listen(3001, () => {
console.log("backend running on Port: 3001");
});
Now I import the checkNotAuth function to the /auth route file and add it to the routes...
const express = require("express");
const { checkAuthenticated, checkNotAuthenticated } = require("../server");
const router = express.Router();
router.post("/login", checkNotAuthenticated, async (req, res) => {
...
return res.status(401).send();
});
module.exports = router;
Now I get this error:
Error: Route.post() requires a callback function but got a [object Undefined]
when I remove the checkNotAuth middleware, there is no error.
How can I fix this?
Because I can't export these two functions from the server.js file for some reason, I just put them in there on file and exported them there. Now I import them in the server and auth route file.
Related
Issue
I am trying to pass my variable, req.app.locals.userId down to the /getuser route. Now I have tried with and without app in req.app.locals.userId. The issue is the variable keeps returning undefined in my /getuser route. I have tried to run the middleware directly in the router and the variable returns correctly, but it seems like req.app.locals does not work when I am using it as a middleware. req.app.locals returns an Object which means that req.app.locals works.
const verifyJWT = (req, res, next) => {
const token = req.headers["x-access-token"]
if (!token) {
res.send("We need a token")
} else {
jwt.verify(JSON.parse(token), "jwtSecret", (err, decoded) => {
if (err) {
res.json({ auth: false, message: "You failed to authenticate" })
} else {
req.app.locals.userId = decoded.id; //This is the variable I am trying to get
next();
}
})
}
}
router.route("/getuser", verifyJWT).get((req, res) => {
console.log(req.app.locals.userId) // <--- Returns undefined
});
I just can't see what I am doing wrong.
My index file
const express = require('express');
const cors = require('cors');
const jwt = require('jsonwebtoken');
const bp = require('body-parser');
const auth = require("./routes/auth");
const PORT = process.env.PORT || 3003;
const app = express();
app.use(cors());
app.use(express.json());
app.use("/auth", auth);
app.get("/", (req, res) => {
console.log("/");
});
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
Index file and your controller appear fine once working on your route file.
router.route("/getuser", verifyJWT).get((req, res) => {
console.log(req.app.locals.userId) // <--- Returns undefined
});
Instead of this try to get req...
router.get("/getuser", verifyJWT, (req, res, next) => {
console.log(req.app.locals.userId);
});
router.route() only takes one argument (the path to match).
Instead, use something like this:
router.route('/getuser').use(verifyJWT);
Or the mostly equivalent:
router.use('/getuser', verifyJWT);
im trying to redirect the user to the dashboard if ever the id is not on database.
but I get an error message saying "Cannot set headers after they are sent to the client"
my middleware code
const mongoose = require('mongoose');
const Room = require('./model/room');
module.exports.isOnDB = async (req, res, next) => {
const { id } = req.params;
console.log(id);
const room = Room.findById(id, function (err) {
console.log('ge');
//console.log(err.message);
res.redirect('/');
});
next();
};
my routes code
const { isOnDB } = require('../middleware');
router.get('/', (req, res) => {
res.render('layouts/boilerplate');
});
router.get('/room/:id', isOnDB, (req, res) => {
res.render('room', { roomId: req.params.room });
});
my app.js
const adminRoutes = require('./routes/admin');
app.use('/', adminRoutes);
The problem is that you are not waiting for your Room.findById function to finish before calling next(). I would move the next() call into the Room.findById callback so you can handle both situations - if the id is in the DB and if it isn't.
For example:
module.exports.isOnDB = async (req, res, next) => {
const { id } = req.params;
const room = Room.findById(id, function (err) {
if (err) {
// if id is NOT found, or if there's an error, call next()
console.log(err);
return next();
}
// if the id is found in the DB, then redirect
res.redirect('/');
});
};
I am learning a new way to authenticate all my APIs using the application-level middleware. I looked into multiple examples. I tried the following code as one of the ways.
Below is my code, I am writing firebase function with the help of necessary fields already there. I use "firebase serve" to host my functions locally.
const express = require('express')
const bodyParser = require('body-parser')
const cookieParser = require('cookie-parser')()
const cors = require('cors')({ origin: true })
const app = express()
const router = express.Router()
app.use(cors)
app.use(cookieParser)
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))
app.use(async (err, req, res, next) => {
console.log('Middleware')
try {
const { authorization } = req.headers
if (!authorization) {
throw new ErrorHandler(401, "User is not unathorized")
}
if (!authorization.startsWith('Bearer')) {
throw new ErrorHandler(401, "User is not unathorized")
}
const split = authorization.split('Bearer ')
if (split.length !== 2) {
throw new ErrorHandler(401, "User is not unathorized")
}
const token = split[1]
const decodedToken = await admin.auth().verifyIdToken(token);
res.setHeader("email", decodedToken.email)
next()
} catch (error) {
console.log("END")
next(error)
}
});
router.get('/', (req, res) => {
res.end(`${Date.now()}`)
})
router.post('/data', async (req, res, next) => {
res.setHeader("Content-Type", "application/json")
console.log('DATA')
try {
// my other logic goes here
res.end()
} catch (error) {
next(error)
}
})
app.use('/api', router)
app.use((err, req, res, next) => {
if (err) {
handleError(err, res);
}
console.log(JSON.stringify(req.body))
});
exports.app = functions.https.onRequest(app)
I have created a cloud function named app. I am using API like this:
http://localhost:5000/app/api/data
I have written a middleware for authorizing all my APIs that are coming. Middleware is fetching bearer token and token is being verified with the help of firebase.
But when I call "/api/data" this API from postman or web the middleware is not called. For debugging purpose I used console.log to check.
My current flow is POSTMAN -> DATA
What I want is:
POSTMAN -> MIDDLEWARE(if authenticated) -> DATA
POSTMAN -> MIDDLEWARE(if not authenticated) -> END
Please let me know what is the issue with my code is.
Remove err parameter from the middleware, You are setting it as an error handler instead of a middleware, this is the reason the code is not getting executed,
Below code will execute the handler every time you access the /api route
const express = require('express')
const bodyParser = require('body-parser')
const cookieParser = require('cookie-parser')()
const cors = require('cors')({ origin: true })
const app = express()
const router = express.Router()
app.use(cors)
app.use(cookieParser)
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))
app.use(async (req, res, next) => {
console.log('Middleware')
try {
const { authorization } = req.headers
if (!authorization) {
throw new ErrorHandler(401, "User is not unathorized")
}
if (!authorization.startsWith('Bearer')) {
throw new ErrorHandler(401, "User is not unathorized")
}
const split = authorization.split('Bearer ')
if (split.length !== 2) {
throw new ErrorHandler(401, "User is not unathorized")
}
const token = split[1]
const decodedToken = await admin.auth().verifyIdToken(token);
res.setHeader("email", decodedToken.email)
next()
} catch (error) {
console.log("END")
next(error)
}
});
router.get('/', (req, res) => {
res.end(`${Date.now()}`)
})
router.post('/data', async (req, res, next) => {
res.setHeader("Content-Type", "application/json")
console.log('DATA')
try {
// my other logic goes here
res.end()
} catch (error) {
next(error)
}
})
app.use('/api', router)
app.use((err, req, res, next) => {
if (err) {
handleError(err, res);
}
console.log(JSON.stringify(req.body))
});
exports.app = functions.https.onRequest(app)
I would like to protect some endpoints in my express app, I want to create something simple to manage if my app became a big app...now I'm doing something like this:
setProtected(router) {
const self = this;
router.use(this.auth);
...
}
setPublic(router) {
const self = this;
...
}
getRouter() {
const router = express.Router();
this.setPublic(router);
this.setProtected(router);
return router;
}
with:
auth(req, res, next) {
if(req.isAuthenticated()) {
console.log('req.isAuthenticated()', req.isAuthenticated());
return next();
}
return res.send(401);
}
the problem in this case is that is difficult maintain and it doesn't work well as if I have /:id in my publicRoute and for example /my-items in my protected route when I'm not logged and I try to reach /my-items I get the code of /:id.
Another idea was to create a json with the list of all my urls with same information like protected/not protected and eventual roles and then change auth with something like:
import urls from './urls';
auth(req, res, next) {
if (urls[req.url] == 'public') {
return next()
}
else if (urls[req.url] == 'protected' && req.isAuthenticated()) {
return next();
}
return res.send(401);
}
whats the best way for you?
You can chain middlewares:
eg.
const authenticate = (req, res, next) {
.. some auth logic
next();
}
app.use('/', main...
app.use('/profile', authenticate, otherMiddleware,
app.use('/admin', authenticate, isAdmin, otherMiddleware...
in your main file (server.js) import the routes and use the middleware there :)
server.js
const express = require('express')
const cors = require('cors')
const app = express()
// import admin routes
const adminRoute = require('./app/routes/admin.route.js')
// Add middleware for parsing URL encoded bodies (which are usually sent by browser)
app.use(cors())
// Add middleware for parsing JSON and urlencoded data and populating `req.body`
app.use(express.urlencoded({ extended: false }))
app.use(express.json())
// homepage route
app.get("/", (req, res) => {
res.json({ message: "Hello World" })
})
// restricted by middleware "isAdmin"
app.use('/api/v1', isAdmin, adminRoute)
app.listen(8008).on('listening', () => {
console.log('Server is running on 8008')
})
admin.route.js
const express = require('express')
const admin = require('../controllers/admin.controller.js')
const router = express.Router()
// get all admin users
router.get('/users', (req, res, next) => {
admin.getAdminUsers(req, res, next)
})
module.exports = router
verify_jwt_token
var jwt = require('jsonwebtoken');
var config = require('./env/config.json')
module.exports = function(req,res,next) {
var token = req.body.token || req.query.token || req.headers['x-access-token'];
if (token) {
console.log("----------------------------------------");
console.log(token);
console.log("----------------------------------------");
console.log(config.jwt_secret);
console.log("----------------------------------------");
// verifies secret and checks exp
jwt.verify(token, config.jwt_secret, function(err, decoded) {
if (err) { //failed verification.
return res.json({"error": true});
}
req.decoded = decoded;
next(); //no error, proceed
});
} else {
// forbidden without token
return res.status(403).send({
"error": true
});
}
}
Routes
var user = require('../controller/user.controller.js');
var token_verify = require('../../config/verify_jwt_token.js')
module.exports = function (app) {
app.route('/register_user').post(user.register);
app.route('/login').get(user.login);
app.route('/auth',token_verify).get(user.auth);
}
I want to call j w t verification function from this route.I don't want to call from the controller. app.route('/auth',token_verify).get(user.auth); Calling from this route
Please help.
Thank you in advance.
This will work-ish:
const express = require('express')
const app = express()
function jwtVerify (req, res, next) {
console.log('verifying token...')
// logic
next()
}
app
.use(jwtVerify)
.route('/example')
.get((req, res) => res.json({ hello: 'World' }))
app.listen(3000)
The issue you will run into is that the middleware will execute for every route you define because the middleware is applied to the app instance. To fix this, you need to make use of the Router. Quick dirty example below:
app.js
const express = require('express')
const app = express()
const securedRoutes = require('./secured-routes')
function jwtVerify (req, res, next) {
console.log('verifying token...')
// logic
next()
}
app.use('/secured', jwtVerify, securedRoutes)
app.listen(3000)
secured-routes.js
const express = require('express')
const router = express.Router()
router.get('/', (req, res) => res.json({ message: 'Hello from secured routes' }))
module.exports = router