I created an express middleware that checks the user token as authorization before proceeding:
import jwt from 'jsonwebtoken';
export const verifyAuthorization = async (req, res, next) => {
try {
const token = req.headers.authorization.split(' ')[1];
if (!token) return res.status(403).send({ error: 'Not authorized' });
const tokenData = jwt.verify(token, 'secret_code');
req.userData = {
userId: tokenData.userId,
fullName: tokenData.fullName,
section: tokenData.section,
groupId: tokenData.groupId,
level: tokenData.level,
};
next();
} catch (error) {
console.log(error);
res.status(403).send({ error: 'Not authorized' });
}
};
I applied this middleware for all endpoints of a route like this:
import { Router } from 'express';
const router = Router();
import item from '../controllers/item_controllers.js';
import { verifyAuthorization } from '../middleware/verifyAuthorization.js';
router.use(verifyAuthorization);
router.post('/all', item.all);
router.post('/new', item.newOne);
router.post('/edit', item.edit);
export default router;
The problem is that if there's a problem with the authorization, it sends back the status code but not the body. In my React app I see the 403 error on console but it logs the response as "undefined".
The controller functions send back the json body perfectly fine. I don't understand what's wrong with the middleware here.
Any ideas??
Thank you
I fixed it. There was a problem with the custom hook I created for http requests. It was finishing the requests on error without sending back any data. That's why I saw the status code error but not the body. I logged the response on the hook and it gets there. I just deleted the "return" keyword on the error block in the hook and I got the response's body perfectly.
Thank you everybody for your time.
Related
I am still learning about the MERN stack, and I am making a simple CRUD app using the MERN stack. I added some validators using express-validator for the requests. The problem is, somehow the data is sent and saved to the database but not giving any response when I try it using Postman.
I think the problem is either on the response of the post controller function or the validators. I got no idea where is it exactly though. I've been stuck here for like an hour. Already going back and forth to get some answers on google. but couldnt find any.
here are the codes.
index.js :
import express from 'express'
import mongoose from 'mongoose'
import dotenv from 'dotenv'
import { router as postRoutes } from './routes/post.js'
import { router as authRoutes } from './routes/auth.js'
//setup
const app = express()
app.use(express.json());
dotenv.config();
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin','*')
res.setHeader('Access-Control-Allow-Methods','POST, GET, PUT, DELETE, OPTIONS')
res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
next();
})
//route groups
app.use('/v1/post', postRoutes)
app.use('/v1/auth', authRoutes)
//validation
app.use((error,req,res,next) => {
const status = error.errStatus || 500
const message = error.message
const data = error.data
res.status(status).json({message : message, data: data})
})
//connect to mongodb
mongoose.connect(process.env.MONGO_URI, { useNewUrlParser: true, useUnifiedTopology: true })
.then(app.listen(4000, ()=>{ console.log('Server is up!')}))
.catch(err => console.log(err))
Post controller :
*Note: I dont send the whole code of the post controller here, only the create post function
import { validationResult } from 'express-validator'
import PostSchema from '../models/post.js'
//handle post requests
const createPost = (req, res, next) => {
const title = req.body.title
const content = req.body.content
//get errors result from express-validator
const errors = validationResult(req)
if(!errors.isEmpty()){
const err = new Error('Invalid request')
err.errStatus = 400
err.data = errors.array()
throw err
}
const Posts = new PostSchema({
title: title,
content: content,
author: {
_id: 1,
name: 'fratio '
}
})
Posts.save()
.then(result =>{
res.status(201).json({ //the error messsage says there is an error on this line
description: "successfully created",
data : result,
})
})
.catch(err =>console.log(err))
next()
}
export {createPost}
Post routes :
import express from 'express'
import { body } from 'express-validator'
import { createPost, deletePost, getPosts, updatePost } from '../controllers/post.js'
const router = express.Router()
router.get('/', getPosts)
router.post('/create',
[
body('title').isLength({min: 5}).withMessage('minimum length is 5 characters'),
body('content').isLength({min: 5}).withMessage('minimum length is 5 characters')
], createPost)
router.put('/:id/update', updatePost)
router.delete('/:id/delete', deletePost)
export { router }
Post Schema :
import mongoose from 'mongoose'
const Schema = mongoose.Schema
const PostSchema = new Schema({
title: {
type : String,
required : true,
},
content: {
type : String,
required : true
},
author: {
type: Object,
required: true
}
},{
timestamp: true
})
export default mongoose.model("PostSchema", PostSchema)
ERROR MESSAGE :
[nodemon] starting `node index.js`
Server is up!
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:561:11)
at ServerResponse.header (D:\Programming\Web Development\mern-playground\mern-basic-1\server\node_modules\express\lib\response.js:771:10)
at ServerResponse.send (D:\Programming\Web Development\mern-playground\mern-basic-1\server\node_modules\express\lib\response.js:170:12)
at ServerResponse.json (D:\Programming\Web Development\mern-playground\mern-basic-1\server\node_modules\express\lib\response.js:267:15)
at file:///D:/Programming/Web%20Development/mern-playground/mern-basic-1/server/controllers/post.js:28:25
at processTicksAndRejections (internal/process/task_queues.js:95:5) {
code: 'ERR_HTTP_HEADERS_SENT'
}
Thank you so much. I hope you guys can help me out, I really got no idea how to fix this. Sorry if the code is hard to read.
You have a problem with your promise logic.
const createPost = (req, res, next) => {
// This will create a promise, when the promise is resolved "then" will be executed
// if the promise is rejected, "catch" will be executed.
// A promise doesn't actually stops the flow of the code here, "next()" will be called
// before the promise is resolved/rejected
Posts.save()
.then(result =>{
res.status(201).json({ //the error messsage says there is an error on this line
description: "successfully created",
data : result,
})
})
.catch(err =>console.log(err));
// "next" is called right after the promise is created
// but the promise doesn't stop the flow of the code
// so this is executed while the promise is still being resolved/reject
next()
}
You are calling next before the promise is resolved/rejected, so the next handler is probably the default 404 handler of express and that is the response you will receive. Later, the promise will be resolved/rejected and you will try to send a response again and that's why the error is thrown.
The solution is simple, move the next() call inside your promise or delete it.
I have a small API made in nodejs with express. A while ago I did not touch it and everything worked perfectly. Only now have I decided to implement JsonWebToken. In Postman, the login works fine, however, when trying to send the token as a header in a request I get an error. When i don't send the token in the request, response successfull (obviously since there is no token, the endpoint returns a 401 to me).
If I try to do it after authenticating (saving the token in an environment variable) and this time assigning it to the header, the following happens
If I send anything if it works, apparently it has to do with the length of the token.
I have tried it outside of postman, and the same thing happens, so the error does not seem to be from postman.
I don't know how to solve the problem, apparently nodejs does not handle the request by the length of the token.Is there a way to expand that?
The nodejs server entry point is:
// Enviroment process
require("dotenv").config();
// Body Parser
const bodyParser = require("body-parser");
const cors = require("cors");
// Express server
const app = require("express")();
app.use(cors());
// BodyParser middleware
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// Routes middleware
app.use(require("./routes/index"));
// Run server
app.listen(process.env.PORT, () => {
console.log(`Escuchando en el puerto ${process.env.PORT}`);
});
Routes:
const express = require("express");
const { checkToken } = require("../middlewares/authentication");
const app = express();
/// Base Routes
app.get(
"/equipments",
[checkToken],
require("../controllers/equipment/get_all.controller")
);
module.exports = app;
The checkToken middleware:
const jwt = require("jsonwebtoken");
const checkToken = (req, res, next) => {
const token = req.get("token") || req.query.token;
jwt.verify(token, process.env.SEED, (err, decoded) => {
if (err) {
return res.status(401).json({
ok: false,
error: {
message: "Invalid Token",
},
});
}
req.user = decoded.user;
next();
});
};
module.exports = {
checkToken,
};
The .env variables:
// Node env (development | production)
NODE_ENV=development
// Server Port
PORT=3000
// Token Time Expiration
TOKEN_EXPIRES=48h
// Token Seed
SEED=exampleseed
UPDATE
When I send the token through the body of the request, the error does not occur, and everything works correctly (obviously changing the middleware so that it receives it by the body). The problem is when I send it by headers or a query parameter.
const checkToken = (req, res, next) => {
// const token = req.get("token") || req.query.token;
const token = req.body.token;
jwt.verify(token, process.env.SEED, (err, decoded) => {
if (err) {
return res.status(401).json({
ok: false,
error: {
message: "Invalid Token",
},
});
}
req.user = decoded.user;
next();
});
};
UPDATE AND SOLUTION:
After trying only those files, I realized that the error did not come from these. The problem was in the authentication. When creating the token I used the information of the logged in user, however, I had not realized that it had a base64 image field.
// ... after login, user object contains object information
let token = jwt.sign(
{
user: {
id: user.id,
name: user.name,
image: user.image.base64Url
},
},
process.env.SEED,
{ expiresIn: process.env.TOKEN_EXPIRES }
);
The length of the base64 image made the token extremely long. Then when making the request and sending a string token with many characters, the reading error occurrs (Error: read ECONNRESET).
The solution was to ignore the image field when creating the token.
Finally, before an error of the same type, check that a field that contains too much information is not being sent.
I have made a custom error handler for my koa app which works beautifully (except for one sticking point) - using ctx.throw() means any stacktrace is emitted to the server logs and also any custom error message is sent in the response.
The one problem is that Content-Type header is text/plain but I really need it to be application/json.
app.js:
import Koa from 'koa';
import bodyParser from 'koa-bodyparser';
import logger from 'koa-morgan';
import authentication from './middleware/authentication';
import config from './config';
import errorHandler from './middleware/error-handler';
import notificationsRoutes from './routes/notifications';
const app = new Koa();
app.use(errorHandler);
app.use(bodyParser());
app.use(logger(config.logLevel));
app.use(authentication);
app.use(notificationsRoutes.routes());
export default app;
error-handler.js:
export default async (ctx, next) => {
return next().catch(({ statusCode, message }) => {
ctx.throw(statusCode, JSON.stringify({ message }));
});
};
(I thought (statusCode, JSON.stringify({ message })); might coerce the response into application/json but it doesn't.
I have googled to no avail. Please help!
Managed to modify the error-handler to produce the desired result. Works really well - stack traces are emitted to server logs and the first line of that message becomes the message in the response body. The latter might be considered a downside by some but it depends what you're after.
error-handler.js:
export default async (ctx, next) => {
return next().catch(err => {
const { statusCode, message } = err;
ctx.type = 'json';
ctx.status = statusCode || 500;
ctx.body = {
status: 'error',
message
};
ctx.app.emit('error', err, ctx);
});
};
Found this and used it for reference: https://github.com/koajs/examples/blob/master/errors/app.js
It's worth mentioning that this custom error - ServerError.js - is used in the app; that's why ctx.status = statusCode || 500 - statusCode is provided by ServerError when used, but for non-custom errors that are thrown, statusCode comes through to error-handler.js as undefined so || 500 is needed.
ServerError.js:
export class ServerError extends Error {
constructor(statusCode, message) {
super(message);
this.statusCode = statusCode;
}
}
(usage: throw new ServerError(400, 'my informative error message');)
Don't have any catch blocks in any of your middlewares and the error will propagate all the way up to your top errorHandler middleware in app.js (which is what you want to happen).
Custom error handling in koa seems to generate many different opinions but this seems to work well for us for now.
Add Error handler middleware
server.use(async function jsonErrorHandler(ctx, next) {
try {
await next();
} catch (err: any) {
ctx.status = err.status || err.statusCode || 500;
ctx.body = { message: err };
ctx.app.emit('error', err, ctx);
}
});
then throw the error like this:
export async function usersIssuesHandler(context: Context) {
....
context.throw('some error here',500);
}
I am a beginner in VueJs and Expressjs. I am trying to make frontend side by Vuejs and backend by ExpressJs. I send a post request to the backend (expressJs) and :
1- Response is undefined
2- At the same time I can see 2 requests in chrome development tools. One is Option and another one is Post.
3- With postman there is no problem at all.
Here is the code of app.js in express
console.log('Server is running')
const express = require('express'),
bodyParser = require('body-parser'),
cors = require('cors'),
morgan = require('morgan');
app = new express();
//Setup middleware
app.use(cors());
app.use(morgan('combined'))
app.use(bodyParser.json())
app.post('/register', (req, res, next) => {
res.send({
message: `Hello ${req.body.email}! your user was registered!`
})
});
app.listen(8081);
And here is the code in VueJs :
// Api Setting
import axios from 'axios'
export const HTTP = axios.create({
baseURL: `http://localhost:8081`
});
// AuthenticationService
import { HTTP } from '../services/Api'
export default {
register(credentials) {
HTTP.post('register', credentials);
}
}
// Register Component
export default {
data() {
return {
email: '',
password: ''
};
},
methods: {
async register() {
const response = await AuthenticationService.register({
email: this.email,
password: this.password
});
console.log(response); // the value is undefined
}
}
};
I really don't know what I missed here that I get an undefined response and 2 requests at the same time. I appreciate any hint.
Whole code on github repo : here
Maybe. Authentication.register is not returning anything or more specifically a Promise which should be used to populate const response in the await call.
Try returning something like so: return HTTP.post('register', credentials); inside register.
For this to work though, HTTP.post('register', credentials) should also return something.
I use JSON.stringify to send the data, you are sending the objects directly, so
register(credentials) {
HTTP.post('register', credentials);
}
becomes
register(credentials) {
HTTP.post('register', JSON.stringify(credentials));
}
I am trying to debug a failing JWT auth setup, which always returns a 401.
My passport setup (middleware/auth.js)
import passport from 'passport'
import { Strategy as JwtStrategy, ExtractJwt } from 'passport-jwt'
module.exports = function() {
var options = {};
options.jwtFromRequest = ExtractJwt.fromAuthHeader()
options.secretOrKey = 'superdupersecret'
var strategy = new JwtStrategy(options, function(payload, done) {
console.log('this is not printing') <---------------
var user = payload.sub || null;
if (user) {
return done(null, { id: user._id });
} else {
return done(new Error("User not found"), null);
}
});
passport.use(strategy);
return {
initialize: () => {
console.log('this only prints on boot'); <---------------
return passport.initialize();
},
authenticate: () => {
console.log('this too') <---------------
return passport.authenticate("jwt", {session: false});
}
};
};
My server.js file where I initialize passport:
import express from 'express'
(...)
var auth = require("./middleware/auth.js")();
// Instantiate app
const app = express();
// Initialize passport for auth use
app.use(auth.initialize())
And my protected route that always returns a 401:
import express from 'express'
var auth = require("../middleware/auth.js")();
const userRouter = express.Router()
userRouter.get('/dashboard', auth.authenticate(), (req, res) => {
res.send('It worked! User id is: ' + req.user + '.')
})
export default userRouter
I have tried to add print statements within the actual passport.js module itself, as well as passport-jwt, with no success.
After the authentication middleware on the protected route, nothing logs.
I have tried a ton of setup permutations over the past 3 days now. Any help would be greatly appreciated
Ok, I followed the tutorial you mentioned and it seems to work.
Here are some notes (some may be obvious, no offense).
Copy exactly the code as the tutorial
After you have everything, you need to "login". Make a POST request to /token. Content type has to be application/json and on the body of the request you need to sent an object with email and password (from tutorial).
After you login, the server returns a token.
Take that token and now make a GET request to /user. In the headers of the request add: Authorization: JWT [your token here]. You have to write "JWT" and the token separated by one space.
The server returns a status 200. I modified so it returns the user.
app.get("/user", auth.authenticate(), function(req, res) {
res.json({user: req.user});
});