I have some routes on my API. And have a middleware. Its creating bearer token and checking it. But i want some of my routes dont enter to that middleware so i can access without token. How can i make it ? My middleware :
app.use(async (req, res, next) => {
if (
req.path === "/api/v1/collection/byhome" || // I dont want that part.
req.path === "/api/v1/user/login" // Its working but its not looks like best solution.
) {
next();
} else {
const bearerHeader = req.header("authorization");
if (typeof bearerHeader !== "undefined") {
const bearer = bearerHeader.split(" ");
const bearerToken = bearer[1];
req.token = bearerToken;
jwt.verify(req.token, process.env.SECRETKEY, async (err, authData) => {
if (err) {
res.sendStatus(401);
} else {
next();
}
});
} else {
res.statusCode = 400;
const Response = {
message: "Invalid Token",
StatusCode: res.statusCode,
};
res.json(Response);
}
}
});
My Route :
app.get(
`/api/${version}/article/bycollection/:id`,
ArticleRoute.getbycollection
);
your way of doing it is correct you can make your code more readable by making an array of all the middleware you want to be out of scope of your middleware
const whiteListEndpoints = ["/api/v1/this", "/api/v1/this1", "/api/v1/this2"]
then
// your middleware
app.use((req, res,next) => {
//if the path was in the whitelist just call next function
if(whiteListEndpoints.includes(req.url)) return next()
// let the middlware do it's job
})
or you can change your express use order
const firstRoute = app.use("/no_middleware", router);
app.use((req, res, next) => {}) // your middleware
const secondRoute = app.use("/with_middleware", router);
here the first router won't use the middleware since it has not yet been called.
You can use Express.Router to achieve the desired result. With express router you can differentiate between routes and have different middlewares for each router.
Follow the steps given below:
Create a auth middleware middlewares/private.authenticate.js
function auth(req, res, next) {
// do auth stuff...
next();
}
Create a file routes/private/index.js
// private route handler
import { Router } from "express";
import auth from "./middlewares/private.authenticate.js";
const router = Router();
router.use(auth); // use auth middleware
router.route("/")
.get()
.put()
export default router;
Create a file routes/public/index.js
import { Router } from "express";
const router = Router();
router.route("/")
.get()
.put()
export default router;
Your express app file
import express from "express";
const app = express();
import PublicRoutes from "./routes/public";
import PrivateRoutes from "./routes/private";
// public routes path
app.use("/api/public", PublicRoutes);
// private routes path
app.use("/api/private", PrivateRoutes);
You can create a route express.Router() and set this to a path, this router have all auth, then create a second express.Router() and this without auth.
var router = express.Router();
// your code for API auth...
router.get('/api/v1/collection/byhome',myMiddleware, (req, res, next) => {
res.send('Hey There');
})
app.use('/api', router);
var routerGuest = express.Router();
//
routerGuest.get('/', (req, res, next) => {
res.send('Hey There');
})
app.use('/guest', routerGuest)
for authentication, I recomend to make a separate middleware and then pass it to our route
function myMiddleware(req, res, next){
const bearerHeader = req.header("authorization");
if (typeof bearerHeader !== "undefined") {
const bearer = bearerHeader.split(" ");
const bearerToken = bearer[1];
req.token = bearerToken;
jwt.verify(req.token, process.env.SECRETKEY, async (err, authData) => {
if (err) {
res.sendStatus(401);
} else {
next();
}
});
} else {
res.statusCode = 400;
const Response = {
message: "Invalid Token",
StatusCode: res.statusCode,
};
res.json(Response);
}
}
}
I think with this you may have some idea to do it :)
Related
I am trying to set up a few pages so that when a user goes to locahost:number/war . They can see the /war page. But when I run the server I get a "Cannot GET war" error on the page. I've set it up similar to this before and didnt have an issue.
I also get a "ReferenceError: __dirname is not defined" issue on the console
import express from 'express';
const app = express();
const router = express.Router();
import path from 'path';
import {getData} from './server.js'
// HTML Routes
router.get('/', (req,res)=> {
res.sendFile(path.join(__dirname, "../start.html"));
})
router.get('/war', (req,res)=> {
res.sendFile(path.join(__dirname, "../index.html"));
})
router.get('/score', (req,res)=> {
res.sendFile(path.join(__dirname, "../finalScore.html"));
})
// Data
export async function sendStats(){
app.get("/data", (req,res)=> {
const data = getData()
res.json(data)
})
app.post("/data",(req, res) => {
const {name, score} = req.body
const data = createData(name, score)
res.json(data)
} )
app.use((err, req, res, next) => {
console.log(err.stack)
res.status(500).send('Something Broke!')
})
app.listen(7171, ()=> {
console.log('Server is running on port 9191')
})
}
const data = await sendStats();
You forgot to load the router in the app:
app.use('/', router)
Here is my server.js:
import express from "express";
import mongoose from "mongoose";
import productRouter from "./routers/productRouter.js";
import dotenv from "dotenv";
dotenv.config();
const app = express();
app.use(express.json());
let prof = process.env.PROF;
mongoose.connect(
`${prof}`
);
// Add headers before the routes are defined
app.use(function (req, res, next) {
// Website you wish to allow to connect
res.setHeader("Access-Control-Allow-Origin", "*");
// Request methods you wish to allow
res.setHeader(
"Access-Control-Allow-Methods",
"GET, POST, OPTIONS, PUT, PATCH, DELETE"
);
res.header("Access-Control-Allow-Headers", "Content-Type");
// Request headers you wish to allow
// res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader("Access-Control-Allow-Credentials", true);
// Pass to next layer of middleware
next();
});
/*
app.get("/api/products", (req, res) => {
res.send(data);
});*/
app.use("/api/products", productRouter);
app.get("/", (req, res) => {
res.send("Server is ready");
});
const port = process.env.PORT || 5000;
app.listen(port);
Going inside productRouter.js ...
import express from "express";
import mongoose from "mongoose";
import data from "../data.js";
import Product from "../models/productModel.js";
import { payment } from "./paymentController.js";
const productRouter = express.Router();
productRouter.use("/pay", async (req, res, next) => {
// dont store cart in db, store it in local storage. On checkout get Id of all items in cart. Then find their prices from the db and charge correctly.
console.log("middleware ran");
console.log(req.body);
const productIdsAndAmounts = req.body.basketItems.items.map((item) => {
return { id: item.id, amount: item.amount };
});
// this works faster (apparently)
/*
const objIds = productIds.map((id) => mongoose.Types.ObjectId(id));
const orderedItems = await Product.find({
_id: { $in: objIds },
});
*/
// this sends more query requests to db
const orderedItems = await Promise.all(
productIdsAndAmounts.map((productId) => {
return Product.findById(productId.id);
})
);
let i = -1;
let productIdsPricesAmounts = [];
orderedItems.forEach((item) => {
i = i + 1;
productIdsPricesAmounts.push({
id: item.id,
price: item.price,
amount: productIdsAndAmounts[i].amount,
});
});
console.log(productIdsPricesAmounts);
const prices = productIdsPricesAmounts.map((item) => {
return item.price * item.amount;
});
const reducer = (prevValue, currValue) => prevValue + currValue;
const totalTotalPrice = prices.reduce(reducer);
console.log(totalTotalPrice);
req.totalPrice = totalTotalPrice;
//console.log(orderedItems);
//console.log(productIdsAndAmounts);
// console.log(req.body.user); // adres
next();
});
productRouter.get("/", async (req, res) => {
const products = await Product.find({});
res.send(products);
});
productRouter.post("/pay", payment);
export default productRouter;
Now to the paymentController.js:
export const paymentController = async (req, res, next) => {
console.log(req.body.basketItems)
/* returns contents of the body like expected, i can do whatever i want with it*/
}
The behaviour, i can get is:
Client sends request to "api/products/pay", i have access to req.body in paymentController.
The behaviour, i want is:
Client sends request to "api/products/pay", the request first goes through a middleware where i do some calculations on it, then i forward the new variable to my paymentController. The problem is req.body is {} in middleware productRouter.use(), but available in paymentController
What am I doing wrong ? I'm new to express and i don't exactly know what I'm doing yes. I want to have access to req.body inside productRouter. I'm guessing i set up the middleware wrong or something like that. But I can't see what i did wrong.
Can You Use
Router For Example :
user.ts
import express from "express";
let UserRoute = express.Router()
UserRoute.get('/',function (req,res) {
res.json('Hello World')
})
export { UserRoute }
App.ts
import express from "express";
import cors from "cors";
import { UserRoute } from "./routes/v1/user"
const http = require('http');
const app = express();
const server = http.createServer(app);
const PORT : string|number = process.env.PORT || 5000;
app.use(cors());
app.use(express.json());
app.use('/',UserRoute)
//Run Server And Listen To Port
server.listen(PORT,()=>{
console.log("Server UP")
})
Guess this is your main route where you want to add middleware function right
Create a middle were function with name "abc" export from there as name "abc"
Now, here in main route you can use that function as a middleware
productRouter.post("/pay",abc, payment);
here abc is your middleware function
I have been working on this problem for several days but I couldn't find any solution.
For each of my function in the route class I have this 2 Middlewares
index.js
const browser = require('browser-detect');
const detectBrowser = (req,res,next) => {
const result = browser(req.headers['user-agent']);
console.log(result);
if (result) {
if (result.name === 'chrome') {
console.log('Browser: '+result.name+ ' OK!');
next();
} else if (result.name=== 'firefox') {
console.log('Browser: '+result.name+ ' OK!');
next();
} else {
res.render('browser-response');
}
}
};
const redirectHome = (req,res,next) => {
if(req.session.token && req.session.firstName && req.session.lastName) {
if (!req.session.checked) {
res.redirect('/term-of-service');
}
else {
res.redirect('/index');
}
} else {
next();
}
};
and a simple function I want to test is:
router.get('/', detectBrowser, redirectHome, (req, res) => {
res.render("login");
});
I plan to use Mocha and Supertest as frameworks to test the router.get(/) function, but couldn't find anyway to mock the browser and the variables of session (session.token, session.firstName, session.lastName, session.checked).
Does anyone have an idea about this?
Thank you in advance.
For this question I already found out a work around solution.
For the middleware detectBrowser we just need to add
} else if (result.name=== 'node') { next(); }
because we are working in the node enviroment so those lines of code will make the test file pass through the middleware.
For the second middleware we just need to provide stubbing session information for the test. Below is an example :
const route = require ("../../routes/index");
const request = require("supertest");
const express = require("express");
const bodyParser = require('body-parser');
const chai = require('chai');
const expect = chai.expect;
//create app with valid credentials (not yet accept term of service )
let mockingLoggedIn;
//setup mocking session
mockingLoggedIn = express();
mockingLoggedIn.use(express.urlencoded({extended: false}));
mockingLoggedIn.set('view engine', 'ejs');
mockingLoggedIn.use(bodyParser.json());
mockingLoggedIn.use(bodyParser.urlencoded({extended: true}));
// lets stub session middleware
mockingLoggedIn.use(function (req, res, next) {
req.session = {};
next();
});
//provide a fake login
mockingLoggedIn.get('*', function (req, res, next) {
// and provide some accessToken value
req.session.token = "test";
req.session.firstName= 'test';
req.session.lastName = 'test';
req.session.checked = false;
next()
});
mockingLoggedIn.use(route);
And in the test we just need to use that stubbed app to make request to the route. For example:
describe("Testing / route and /login route works", () => {
it('testing index route: GET / should return status 200 and render login site',
(done)=> {
request(mockingLoggedIn) // request an app with stubbed credentials
.get("/")
.expect("Content-Type", "text/html; charset=utf-8")
.expect(200)
.end(function (err,res) {
expect(err).to.be.null;
done();
})
});
});
Hopefully this will help someone :)
This is my index.js file.
import "babel-polyfill";
import http from "http";
import express from "express";
import cors from "cors";
import bodyParser from "body-parser";
import middleware from "./middleware";
import create from "./api/create";
import envVariables from "./envVariables";
import db from "./db";
let app = express();
app.server = http.createServer(app);
// 3rd party middleware
app.use(
cors({
exposedHeaders: envVariables.corsHeaders
})
);
app.use(
bodyParser.json({
limit: envVariables.bodyLimit
})
);
app.use(middleware({ envVariables, db }));
// api router
app.use("/api/create", create({ envVariables, db }));
//error handling
app.use((err, req, res, next) => {
res.status(400).json(err);
});
app.server.listen(process.env.PORT || envVariables.SERVER_PORT, () => {
console.log(`Started on port ${app.server.address().port}`);
});
export default app;
Before calling 'api/create' route, I want a middleware to be called in which I have to validate my key.
Below is my middleware index.js
const axios = require('axios');
import { Router } from 'express';
const getData = async() => {
try {
return axios.post(`https://ilp-ulamp/api/fetchToken`).then(response => {
return response.data;
}).catch(error => {
console.log(error)
})
} catch (error) {
console.log(error);
}
}
export default ({ config, db }) => {
let routes = Router();
let validateKey = async function(req, res, next) {
if(!req.body.authToken)
return res.status(401).send("Access denied. No token provided.");
else {
const resp = await getData();
if(resp.length === 0){
return res.status(403).send("Access denied");
}
else {
next();
}
}
}
return validateKey;
}
Here, the problem is, I am not getting the proper response when calling https://ilp-ulamp/api/fetchToken endpoint. I checked in Postman, this API is giving proper response but when calling this inside a middleware, it is giving response as empty.
I am not sure that I am doing it properly.
Kindly help me with this issue.
Thanks in Advance!!
I use node.js, passport and jwt bearer token to protect my routes. What I don't have yet is rate limiting and blocking of ip/user if too many false attempts. What's the best way to implement it for my setup?
I want to give it a try with rate-limiter-flexible. But how can I integrate e.g. the Login Example from here: https://github.com/animir/node-rate-limiter-flexible/wiki/Overall-example#login-endpoint-protection in my setup below?
helpers/rateLimiter.js
const express = require('express');
const redis = require('redis');
const { RateLimiterRedis } = require('rate-limiter-flexible');
/* What goes here? Example https://github.com/animir/node-rate-limiter-flexible/wiki/Overall-example#login-endpoint-protection doesn't seem to apply */
Those are my routes:
routes/index.js
const express = require('express');
const router = require('express-promise-router')();
const passport = require('passport');
const passLogin = passport.authenticate('local-login', { session: false, failWithError: true });
const { rateLimiter } = require('../helpers/rateLimiter');
...
router.route('/v1/login')
.post( rateLimiter, passLogin, function(err, req, res, next) {
return res.status(401).send({ status: 401, success: false })
}, controller.login );
router.route('/v1/abc/search')
.post( passJWT_ABC, function(err, req, res, next) {
return res.status(401).send({ status: 401, success: false })
}, controller.search );
You should export middleware in this case.
const express = require('express');
const redis = require('redis');
const { RateLimiterRedis } = require('rate-limiter-flexible');
async function loginRoute(req, res) {
// code from example https://github.com/animir/node-rate-limiter-flexible/wiki/Overall-example#login-endpoint-protection
}
export default async (req, res, next) => {
try {
await loginRoute(req, res);
next();
} catch (err) {
res.status(500).end();
}
}
And then you should take care of how authorise(), user.isLoggedIn and user.exists checks work with your application login approach.
There is an example with passport-local, should be useful for you as well https://github.com/passport/express-4.x-local-example/blob/67e0f735fc6d2088d7aa9b8c4eb25bc0052653ec/server-secure.js