express jwt - Authentication Middleware Issue - javascript

I'm setting up a Token based authentication using express-jwt but the middleware which sends an error message if the token is missing or invalid is not working.
index.js file
const express = require('express');
const router = express.Router();
const {getAllUsers: findUser} = require('../controllers/users');
const {register: registerUser, login: loginUser} = require('../controllers/authentication');
const jwt = require('express-jwt');
const auth = jwt({
secret: process.env.JWT_SECRET,
userProperty: 'auth'
});
// users
router
.route('/users', auth)
.get(findUser);
// registration
router
.route('/register')
.post(registerUser);
// login
router
.route('/login',auth)
.post(loginUser);
module.exports = router;
Users Controller:
const mongoose = require('mongoose');
const User = mongoose.model('Users');
let getAllUsers = (req,res) => {
User.find((err,user)=>{
if(user){
res
.status(200)
.json({user})
}
});
};
module.exports = {
getAllUsers
};
app.js file:
require('dotenv').load();
const express = require('express');
const path = require('path');
const favicon = require('serve-favicon');
const logger = require('morgan');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const passport = require('passport');
require('./app_api/models/db');
require('./app_api/config/passport');
const index = require('./app_server/routes/index');
const apiRoutes = require('./app_api/routes/index');
const app = express();
// view engine setup
app.set('views', path.join(__dirname, 'app_server','views'));
app.set('view engine', 'ejs');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(passport.initialize());
app.use('/', index);
app.use('/api', apiRoutes);
//error handlers
// catch unauthorised errors
app.use(function (err, req, res, next) {
if (err.name === 'UnauthorizedError') {
res.status(401).send('invalid token...');
}
});
// catch 404 and forward to error handler
app.use(function(req, res, next) {
const err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
I am setting the middleware on users route which returns a list of all users. But only authorized users should be able to access that list.
The middleware does not seem to be working as I can still get the users list even if I do not send a token.
What am I doing wrong?
Please Note: Im using POSTMAN to test this.
Update (Figured out the problem):
It was a very simple fix can't believe I didn't see it before. The issue was where I was placing auth.
I was placing it after the route url like so:
router
.route('/users', auth)
.get(findUser);
When the correct way of doing this is:
router
.route('/users')
.get(auth, findUser);
The above fixed my issue.

According to README in the repository, you should check if user property is present in the request.
The JWT authentication middleware authenticates callers using a JWT.
If the token is valid, req.user will be set with the JSON object
decoded to be used by later middleware for authorization and access
control.
Your findUser function should handle it
function findUser(req, res) {
if (!req.user) return res.sendStatus(401);
// do something else
}
You might also consider changing userProperty to requestProperty.

Related

Routing keeps returning 404 instead of displaying other views besides index, users, and error in Express.JS

I am setting up a site on Express.JS with express-generator but ran into a hiccup with the routing. Currently I keep receiving the "error" view and a 404 message whenever sending a GET request to another route I set up ("/hook"). Currently the two routes that are working is "/" which goes to the "index" and "/users" which goes responds a message. I would like to see why I am getting this error as the others work.
Here is the app.js file:
var createError = require("http-errors");
var express = require("express");
var path = require("path");
var cookieParser = require("cookie-parser");
var logger = require("morgan");
var indexRouter = require("./routes/index");
var usersRouter = require("./routes/users");
var hookRouter = require("./routes/hook");
var app = express();
// view engine setup
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "ejs");
app.use(logger("dev"));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, "public")));
app.use("/", indexRouter);
app.use("/users", usersRouter);
app.use("/hook", hookRouter);
// catch 404 and forward to error handler
app.use(function (req, res, next) {
next(createError(404));
});
// // error handler
app.use(function (err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get("env") === "development" ? err : {};
// // render the error page
res.status(err.status || 500);
res.render("error");
});
module.exports = app;
Here is the index router
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
module.exports = router;
Here is the hook router:
var express = require("express");
var router = express.Router();
/* GET hook page. */
router.get("/hook", function (req, res) {
res.send("Hook Page Works");
});
module.exports = router;
Please let me know there is any more info I can give. Thanks!
The hook router needs to be:
/* GET hook page. */
router.get("/", function (req, res) {
res.send("Hook Page Works");
});
This:
app.use("/hook", hookRouter);
has already used the /hook part of the path so within the router, you just want /.
Your original combination of:
app.use("/hook", hookRouter);
router.get("/hook", ...);
will respond to the URL /hook/hook.

Axios with Express "Request failed with status code 404"

I'm a new developer trying to understand routers and controllers with Express, Express Router, and Axios.
server (app.js):
var createError = require('http-errors');
var express = require('express');
var cors = require('cors');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var indexRouter = require('./routes/index');
var authRouter = require('./routes/auth');
var app = express();
app.use(cors());
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
// routes
app.use('/', indexRouter);
app.use('/login', authRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
router (auth.js):
var express = require('express');
var router = express.Router();
const controller = require("../controllers/auth.controller");
router.post('/login', controller.login);
module.exports = router;
controller (auth.controller.js):
var axios = require('axios');
const url = "https://my-api-url.com";
module.exports = {
login: (req, res) => {
console.log('made login request');
return axios.post(url + "/token", {
username: req.body.username,
password: req.body.password
}, {
headers: { "Content-Type": "application/json" },
}).then((response) => {
res.send({
access_token: response.data.access_token
});
}).catch((err) => {
console.log('failed to login');
});
}
};
I receive a "POST localhost 404 (Not Found)" and "Uncaught (in promise) Error: Request failed with status code 404" response on the console. However, this works perfectly:
app.js (without router and controller):
var createError = require('http-errors');
var express = require('express');
var axios = require('axios');
var cors = require('cors');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var app = express();
app.use(cors());
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
const url = "https://my-api-url.com";
// returns access token
app.post('/login', (req, res) => {
console.log('made login request!');
axios.post(url + "/token", {username: req.body.username, password: req.body.password}, {
headers: {"Content-Type": "application/json"},
}).then((response) => {
res.send({access_token: response.data.access_token});
}).catch((err) => {
console.log('failed to login');
});
});
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
I could just continue using the last code segment since it works perfectly fine, but I want to learn the logic and best practices. I'm just not sure what I'm doing wrong. Thanks in advance.
In app.js, when you used a router:
app.use('/login', authRouter);
Your application will get any route inside your router in relative way.
When inside your router you handle post that way:
router.post('/login', controller.login);
Your application will wait a call like this: http://localhost:3000/login/login/
If you want to call with http://localhost:3000/login/, your router should be like this:
var express = require('express');
var router = express.Router();
const controller = require("../controllers/auth.controller");
router.post('/', controller.login);
module.exports = router;

How to debug EJS

There are several answers for this question, but they're all several years old and no longer relevant/safe to use.
Since EJS is rendered as html in the browser, there's no way to inspect it in the browser dev tools
The EJS.co site says that the errors/logs should show in the terminal just like node errors, but that's not the reality for me, somehow.
I used the command npx express-generator to bootstrap my project, and I'm wondering if there isn't a debugger of some kind in there that is overriding how EJS is supposed to send errors?
I say this because when EJS has an error, the only thing my terminal shows is something like GET /users/dashboard 500 26.5 ms with nothing else. And then of course the browser shows the default message for 500 errors, which isn't helpful.
I'll throw in my app.js in case someone smarter than me can see the issue there:
const createError = require('http-errors');
const express = require('express');
const path = require('path');
const rfs = require('rotating-file-stream')
const cookieParser = require('cookie-parser');
const logger = require('morgan');
const mongoose = require('mongoose');
require('mongoose-type-url');
// const serveFavicon = require('serve-favicon');
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const expressSession = require('express-session');
const methodOverride = require('method-override');
const expressSanitizer = require('express-sanitizer');
const sgMail = require('#sendgrid/mail');
// create a rotating write stream
const accessLogStream = rfs.createStream('access.log', {
interval: '1d', // rotate daily
path: path.join(__dirname, 'log')
});
const User = require('./models/user');
const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');
const formsRouter = require('./routes/forms');
const companiesRouter = require('./routes/companies');
const locationsRouter = require('./routes/locations');
const app = express();
if (app.get('env') == 'development'){ require('dotenv').config(); };
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
//connect to database
mongoose.connect(process.env.DATABASE_URL,{
useNewUrlParser:true,
useUnifiedTopology:true,
useFindAndModify: false,
useCreateIndex:true
}).then(() => {
console.log('Connected to Mongo DB')
}).catch(err => {
console.log('error: ',err.message)
});
// view engine setup
//use ejs-locals for all ejs templates
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev', { skip: function (req,res) { return res.statusCode < 400} }));
app.use(logger('common', { stream: accessLogStream }));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(methodOverride("_method"));
app.use(expressSanitizer());
var expiryDate = new Date(Date.now() + 60 * 60 * 1000 * 6) // 6 hours
app.use(expressSession({
secret:"surfs up brah",
resave:false,
saveUninitialized:false,
name: 'sessionId',
secure:true,
httpOnly:true,
expires: expiryDate
}));
app.use(passport.initialize());
app.use(passport.session());
app.disable('x-powered-by');
app.use(function(req, res, next){
res.locals.currentUser = req.user;
next();
});
// CHANGE: USE "createStrategy" INSTEAD OF "authenticate"
passport.use(User.createStrategy({usernameField:'personalEmail'}));
// use static authenticate method of model in LocalStrategy
passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
//set local variables middleware
app.use(async function (req,res,next) {
if (app.get('env') == 'development'){ req.user = await User.findOne({firstName: 'potato'}); };
res.locals.currentUser = req.user;
//set default page title if one is not specified
res.locals.title='Custom Forms';
//set success flash message
res.locals.success = req.session.success || "";
//delete flash message after sending it to the page so it doesn't show again
delete req.session.success;
//set error flash message
res.locals.error = req.session.error || "";
//delete flash message after sending it to the page so it doesn't show again
delete req.session.error;
//continue on to the next function in the middlware/route chain
next();
});
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/forms', formsRouter);
app.use('/companies', companiesRouter);
app.use('/locations', locationsRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
const { seedDatabase, clearDatabase, seedDefaultQuestions, clearRecentItems} = require('./seeds.js');
async function databaseInit() {
await clearRecentItems();
// await seedDefaultQuestions();
// await clearDatabase();
await User.register({firstName: 'potato', lastName:'head',username:'potatohead', personalEmail:'test#test.com', role:'Owner'},'password');
// await seedDatabase();
}
databaseInit();
let port = process.env.PORT;
if (port == null || port == "") {
port = 8080;
}
app.listen(port, () => {
console.log("server has started, listening on port "+port);
});
module.exports = app;
Here's an example route:
app.get('/dashboard', (req,res) => {
const currentCompany = Company.findById(req.params.id);
res.render('../views/company/profile', {currentCompany});
}
And some sample EJS:
<html>
<body>
<h1><%= currentCompany.name %></h1>
</body>
</html>
Now say I didn't wrap the findById call in a try block and it didn't find a document, then tried to render the page using an undefined or null object. The EJS file wouldn't have anything to which to refer for currentCompany.name (by the way, I do indeed try/catch and handle errors).
This is where the terminal just logs the simple 500 error, and refuses to load anything at all.
Another example of issues I've had with EJS:
<html>
<body>
<&- include('../../partials/header) %>
</body>
</html>
Here, it would throw the exact same 500 error because I went up one too many folders in the path for that partial. Absolutely nothing else shown anywhere.
Thanks to #LawrenceCherone, the issue is the default error handler created when npx express-generator is used.
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
Without that, the EJS error information (including line number, etc) gets displayed in the browser.

Get 404 render view ejs with express js

I use express ejs on my backend and frontend. I have made a route to display the dashboard on the admin page. but I get 404 error to render view when I enter my url http: // localhost: 3000 /admin, here are some of my code:
app.js
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var indexRouter = require('./routes/index');
var admin = require('./routes/admin');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(indexRouter);
app.use('/admin', admin);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// app.use((req, res, next) => {
// res.status(404).render('error/404')
// });
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error/404');
});
module.exports = app;
controller dashboard.js
exports.getDashboard = (req, res, next) => {
res.render('dashboard', {
pageTitle: 'Dashboard',
path: 'admin/page/dashboard'
});
};
my admin route admin.js
const path = require('path');
var express = require('express');
const adminController = require('../controllers/backend/dashboard');
var router = express.Router();
/* GET users listing. */
router.get('/admin', adminController.getDashboard);
module.exports = router;
thank you, please help me for this issue
in app.js you already have route /admin then again in admin.js, so it will be accessible with localhost:3000/admin/admin.
Change your admin.js to
const path = require('path');
var express = require('express');
const adminController = require('../controllers/backend/dashboard');
var router = express.Router();
/* GET users listing. */
router.get('/', adminController.getDashboard);
module.exports = router;
The problem is here:
const adminController = require('../controllers/backend/dashboard');
You are not exporting a default option from controller dashboard so you need to add curly braces like this:
const { adminController } = require('../controllers/backend/dashboard');
Try this and let me know! :)

Expressjs: throw new TypeError('Router.use() requires a middleware function but got a ' + gettype(fn))

I have been having some problems with trying to get my code to work. I have tried a couple things but and it says my error is in line 28: "app.use('/','indexRouter')" but I have no clue why. My index.js file and app.js file are copies of each other.
My app.js file:
let express = require('express')
let path = require('path')
let favicon = require('serve-favicon')
let cookieParser = require('cookie-parser')
let bodyParser = require('body-parser')
let logger = require('morgan')
//Starts an express app
let app = express()
//Gives access to routes
//You will seperate each route base on different activites and group them that way
let indexRouter = require('./routes/index')
let userRouter = require('./routes/users')
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(app.router);
routes.initialize(app);
app.use('/','indexRouter')
app.use('/users','userRouter')
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
//ALlows www to get access to it
module.exports = app
index.js file:
var express = require('express');
var router = express.Router();
/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('index js stuff');
});
module.exports = router;
indexRouter and userRouter should not be a string.
app.use('/', indexRouter)
app.use('/users', userRouter)

Categories

Resources