Nodejs authentication middleware not working - node.js

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)

Related

Express make try/catch block into middleware to avoid duplication

Im writing my expressJs application, and Im finding in my routes controller the same duplicated code for catching exception, I was wondering how to avoid this.
I have checked this thread, but I get this error "Cannot read property 'catch' of undefined" : Express Try and Catch in Form of middleware
this is my route.js
const express = require("express");
const createHttpError = require("http-errors");
const Validator = require("../middlewares/Validator");
const TaskNotFoundException = require("../services/TaskNotFoundException");
const TaskService = require("../services/TaskService");
router.get("/tasks", async (req, res, next) => {
try {
const data = await TaskService.getTasks();
res.send({ code: 200, message: "Success", data });
} catch (error) {
next(createHttpError(500));
}
});
router.get("/task/:id", async (req, res, next) => {
const { id } = req.params;
try {
const data = await TaskService.getTask(id);
res.send({ code: 200, message: "Success", data });
} catch (error) {
if (error instanceof TaskNotFoundException) {
next(createHttpError(404));
} else {
next(createHttpError(500));
}
}
});
and the list goes on
as you see in all my routes I have a try catch block with the possible errors (either only a 500, or a 500/404). And I would like to avoid this repetition.
this is my app.js
const express = require("express");
const bodyParser = require("body-parser");
const createHttpError = require("http-errors");
const api = require("./routes/api");
const app = express();
app.use(express.json());
app.use(bodyParser.json());
app.use("/api", api);
// Catch HTTP 404
app.use((req, res, next) => {
next(createHttpError(404));
});
// Error Handler
app.use((err, req, res, next) => {
res.status(err.status || 500);
res.json({
error: {
status: err.status || 500,
message: err.message,
},
});
});
module.exports = app;
Like I said, it works perfectly now, I would just like to try to avoid the try catch code duplication, and Ive checked the other questions in Stackoverflow but havent helped. The solution ive linked returns a 500 with this catch undefined message (which is not what I want) and on other routes that also have a 404 it just doesnt work.
Thanks a lot!
Update:
I followed Heikos advice but still not working
api.js
const express = require("express");
const createHttpError = require("http-errors");
const Validator = require("../middlewares/Validator");
const TaskNotFoundException = require("../services/TaskNotFoundException");
const TaskService = require("../services/TaskService");
const router = express.Router();
router.get("/tasks", async (req, res, next) => {
const data = await TaskService.getTasks();
res.send({ code: 200, message: "Success", data });
});
app.js
const express = require("express");
const bodyParser = require("body-parser");
const createHttpError = require("http-errors");
const api = require("./routes/api");
const app = express();
app.use(express.json());
app.use(bodyParser.json());
app.use("/api", api);
function catchAsyncErrors(middleware) {
return async function(req, res, next) {
try {
await middleware(req, res, next);
} catch(err) {
next(err);
}
};
}
// Catch HTTP 404
app.use(catchAsyncErrors((req, res, next) => {
next(createHttpError(404));
}));
// Error Handler
app.use(catchAsyncErrors((err, req, res, next) => {
res.status(err.status || 500);
res.json({
error: {
status: err.status || 500,
message: err.message,
},
});
}));
module.exports = app;
If the code inside your async middleware functions contains an await, you must also wrap it in a try-catch block, otherwise a rejected promise will be unhandled. For example:
app.use(async function(req, res, next) {
try {
await Promise.reject("error");
} catch(err) {
next(err);
}
});
propagates the error to the error handler, but without the try-catch block it leads to an "UnhandledPromiseRejection".
You can save some typing if you wrap your middleware into a catchAsyncErrors function:
function catchAsyncErrors(middleware) {
return async function(req, res, next) {
try {
await middleware(req, res, next);
} catch(err) {
next(err);
}
};
}
router.get("/tasks", catchAsyncErrors(async (req, res, next) => {
const data = await TaskService.getTasks();
res.send({ code: 200, message: "Success", data });
}));

How to use express middleware on router level

I'm trying to add a simple middleware function for every request on the router level.
The docs are stating:
a middleware function with no mount path will be executed for every
request to the router
In my application I have only one router with one endpoint that is listening for every request and I'm placing my middleware function above this endpoint, but the middleware never gets launched.
Express setup:
const initializeExpress = (): void => {
const port = process.env.PORT;
const app = express();
app.use(helmet());
app.use(express.json());
app.use(cors());
app.use('/api', Router);
app.listen(port, () => {
console.log(`Listening for requests at http://localhost:${port}`);
});
};
My router code:
const Router = express.Router();
Router.use((req, res, next) => {
const token = req.header('authorization');
if (!token) res.status(401).send({ message: 'Unauthorized' });
const isAuthenticated = isAuthorized(token!);
if (isAuthenticated) {
next();
} else {
res.status(401).send({ message: 'Unauthorized' });
}
});
Router.get(
'/:arg1/:arg1Id?/:arg2?/:arg2Id?/:arg3?/:arg3Id?/:arg4?/:arg4Id?',
async (req, res): Promise<void> => {
const routeParams = filterRouteParams(req.params);
const path = basePath + getPathFromRouteParams(routeParams) + '/data.json';
if (await pathExists(path)) {
const data = await getJsonFromPath(path);
if (!isEmpty(data)) {
res.status(200).json(data);
return;
}
res.status(400).send({ message: 'Data not found' });
}
}
);
What am I doing wrong here?
On which route the middleware will be active, you need to define it.
There's two way to make this
First, call middleware before the your route:
const Router = express.Router();
const myMiddleware = (req, res, next) => {
const token = req.header('authorization');
if (!token) res.status(401).send({ message: 'Unauthorized' });
const isAuthenticated = isAuthorized(token!);
if (isAuthenticated) {
next();
} else {
res.status(401).send({ message: 'Unauthorized' });
}
}
Router.get(
'/:arg1/:arg1Id?/:arg2?/:arg2Id?/:arg3?/:arg3Id?/:arg4?/:arg4Id?',
myMiddleware(), //Call middleware here
async (req, res): Promise<void> => {
const routeParams = filterRouteParams(req.params);
const path = basePath + getPathFromRouteParams(routeParams) + '/data.json';
if (await pathExists(path)) {
const data = await getJsonFromPath(path);
if (!isEmpty(data)) {
res.status(200).json(data);
return;
}
res.status(400).send({ message: 'Data not found' });
}
}
);
Second, calling middleware for all routes that you define:
const Router = express.Router();
const myMiddleware = (req, res, next) => {
const token = req.header('authorization');
if (!token) res.status(401).send({ message: 'Unauthorized' });
const isAuthenticated = isAuthorized(token!);
if (isAuthenticated) {
next();
} else {
res.status(401).send({ message: 'Unauthorized' });
}
}
Router.use('/:arg1/:arg1Id?/:arg2?/:arg2Id?/:arg3?/:arg3Id?/:arg4?/:arg4Id?', myMiddleware());
Router.get(
'/:arg1/:arg1Id?/:arg2?/:arg2Id?/:arg3?/:arg3Id?/:arg4?/:arg4Id?'
async (req, res): Promise<void> => {
const routeParams = filterRouteParams(req.params);
const path = basePath + getPathFromRouteParams(routeParams) + '/data.json';
if (await pathExists(path)) {
const data = await getJsonFromPath(path);
if (!isEmpty(data)) {
res.status(200).json(data);
return;
}
res.status(400).send({ message: 'Data not found' });
}
}
);

Why my error is not being handled in express.js?

I am learning express.js, and I am thinking why I receive UnhandledPromiseRejectionWarning and no error response is being sent after executing http api call..
For error handling I have created helper class and middleware:
/helpers/errorHander.js
------------------------------------
class ErrorHandler extends Error {
constructor(statusCode, message) {
super();
this.statusCode = statusCode;
this.message = message;
}
}
const handleError = (err, res) => {
const { statusCode, message } = err;
res.status(statusCode).json({
status: 'error',
statusCode,
message
});
};
module.exports = {
ErrorHandler,
handleError
};
I have enabled this middleware in app.js file:
/app.js
------------------------------------
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const config = require('./config/init');
const cors = require('cors');
const { handleError, ErrorHandler } = require('./helpers/errorHandler');
//routes
const apiRoutes = require('./api');
// connect db
config.initializeDB();
// configure bodyParser
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// Enable Cors
app.use(cors());
// Set Routes
app.use('/', apiRoutes);
app.get('/error', (req, res) => {
throw new ErrorHandler(500, 'Internal server error');
});
// Enable error handling middleware
app.use((err, req, res, next) => {
handleError(err, res);
});
module.exports = app;
When I perform request to the /error endpoint, it works correctly:
/error endpoint
But when performing request to the endpoint which performs operations with mongoose I am not getting desired result:
/v1/users/ endpoint
user.route.js:
const router = require('express').Router();
const userController = require('./user.controller');
router.get('/', userController.getUsers);
router.post('/', userController.createUser);
module.exports = router;
user.controller.js:
const userService = require('../../../services/user');
module.exports = {
getUsers: (req, res, next) => {
return res.json(userService.getUsers());
},
createUser: (req, res, next) => {
const username = req.body.username;
const password = req.body.password;
res.json(userService.createUser(username, password));
}
};
user.service.js:
const User = require('../models/user');
const { ErrorHandler } = require('../helpers/errorHandler');
module.exports = {
getUsers: async () => {
return User.find({});
},
createUser: async (username, password) => {
const user = new User({ username, password });
try {
await user.save();
return user;
} catch (err) {
if (err.code === 11000) {
throw new ErrorHandler(409, 'Username already exists!');
}
throw new ErrorHandler(500, 'Internal server error');
}
}
};
Console gives such a warning:
Console Output
Why it is not working as I want, and how can I make it work?
=========================================================
Update #1
As user - jfriend00 suggested, I tried to await the promise, and here my code looks like on user.controller.js:
const userService = require('../../../services/user');
module.exports = {
getUsers: (req, res, next) => {
return res.json(userService.getUsers());
},
createUser: async (req, res, next) => {
const username = req.body.username;
const password = req.body.password;
try {
let user = await userService.createUser(username, password);
} catch (err) {
next(err);
}
}
};
And Now I get the desired outcome.

Why am I getting a 401 Unauthorized Error when hitting my protected endpoint at /api/protected?

I am using JWT to generate a token for access control. I can hit /api/auth/login and get back the token, however, when attempting to hit /api/protected with a GET request, I get 401 Unauthorized.
I've looked through SO and haven't found anything specific although it seems like a routine issue, maybe. I have tried moving the route around in the server.js file to see if that is the issue . I have removed the preceeding slash from the route (from /api/protected to api/protected) and using the latter I get back a bunch of html due to, I think, the app.use(express.static....
I am using Postman to test it but i'm not sure what I'm missing here. I have also made sure to set the authorization to Bearer Token in Postman.
'use strict';
const { Strategy: LocalStrategy } = require('passport-local');
// Assigns the Strategy export to the name JwtStrategy using object destructuring
const { Strategy: JwtStrategy, ExtractJwt } = require('passport-jwt');
const { User } = require('../users/models');
const { JWT_SECRET } = require('../config');
const localStrategy = new LocalStrategy((username, password, callback) => {
let user;
User.findOne({ username })
.then(_user => {
user = _user;
if (!user) {
// Return a rejected promise so we break out of the chain of .thens.
// Any errors like this will be handled in the catch block.
return Promise.reject({
reason: 'LoginError',
message: 'Incorrect username or password'
});
}
return user.validatePassword(password);
})
.then(isValid => {
if (!isValid) {
return Promise.reject({
reason: 'LoginError',
message: 'Incorrect username or password'
});
}
return callback(null, user);
})
.catch(err => {
if (err.reason === 'LoginError') {
return callback(null, false, err);
}
return callback(err, false);
});
});
const jwtStrategy = new JwtStrategy(
{
secretOrKey: JWT_SECRET,
// Look for the JWT as a Bearer auth header
jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('Bearer'),
// Only allow HS256 tokens - the same as the ones we issue
algorithms: ['HS256']
},
(payload, done) => {
done(null, payload.user);
}
);
module.exports = { localStrategy, jwtStrategy };
'use strict';
//How does order of code affect how it works?
// YES
require('dotenv').config();
const express = require('express');
const mongoose = require('mongoose');
const morgan = require('morgan');
const passport = require('passport');
const path = require('path');
const { router: usersRouter } = require('./users');
const { router: authRouter, localStrategy, jwtStrategy } = require('./auth');
mongoose.Promise = global.Promise;
// Is this needed if dotenv is in this file also?
const { PORT, DATABASE_URL } = require('./config');
const app = express();
// Logging
app.use(morgan("common"));
// const logRequest = (req, res, next) => {
// const now = new Date();
// console.log(
// `local log - ${now.toLocaleDateString()} ${now.toLocaleTimeString()} ${req.method} ${req.url}`
// );
// next();
// }
app.use(function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Content-Type,Authorization');
res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,PATCH,DELETE');
if (req.method === 'OPTIONS') {
return res.send(204);
}
next();
});
passport.use(localStrategy);
passport.use(jwtStrategy);
//app.use(logRequest);
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use('/api/users/', usersRouter);
app.use('/api/auth/', authRouter);
app.use("/api/items", require('./routes/api/items'));
// protected route that needs a valid JWT for access
const jwtAuth = passport.authenticate("jwt", { session: false });
// route to handle static content ie.e *.jpg
app.use(express.static(path.join(__dirname, "client", "build")));
app.get('/api/protected', jwtAuth, (req, res) => {
return res.json({
data: 'Hello World'
});
});
// have react client handle all additional routes
app.get("*", (req, res) => {
res.sendFile(path.join(__dirname, "client", "build", "index.html"));
});
let server;
function runServer(DATABASE_URL, port = PORT) {
return new Promise((resolve, reject) => {
// How is DATABASE_URL used? What is the value? Is it referencing
// DATABASE_URL?
mongoose.connect(DATABASE_URL, { useNewUrlParser: true, useFindAndModify: false }, (err) => {
console.log("Success");
if (err) {
return reject(err);
}
server = app.listen(port, () => {
console.log(`Your app is listening on port ${PORT}`);
resolve();
})
.on('error', (err) => {
mongoose.disconnect();
reject(err);
});
});
});
}
function closeServer() {
return mongoose.disconnect()
.then(() => new Promise((resolve, reject) => {
console.log("Closing server");
server.close((err) => {
if (err) {
return reject(err);
}
resolve();
});
}));
}
if (require.main === module) {
runServer(DATABASE_URL)
.catch(err => console.error(err));
}
module.exports = { app, runServer, closeServer };
enter code hereI am expecting to get back a string that says "Hello World" just to make sure i'm hitting the endpoint correctly. Instead I get the 401 error, GET /api/protected HTTP/1.1" 401enter code here

How to call jwt verification function from routes?

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

Resources