I am using express to create an API.
I am receiving data as a multipart form. I want to upload an image but only after the user is authenticated.
This is the middleware I have created to authenticate the user:
const jwt = require('jsonwebtoken');
const pool = require('../utils/connectionPool');
function authorizeUser(req, res, next){
try{
const authHeader = req.headers.authorization;
const token = authHeader && authHeader.split(' ')[1];
if(token==null) return res.json({
status: 000,
msg: "Token Missing"
});
//TODO: Change secret.
jwt.verify(token, 'mysecret', async (err, user)=>{
if(err){
// console.log(err);
res.json({
status: 001,
msg: "Invalid Token."
});
return;
}
if(req.body.id == undefined || req.body.id != user.id){
res.json({
status: 002,
msg: "User not authenticated."
});
return;
}
let login_timestamp = user.login_timestamp;
//Check login token in db.
let checkToken = await pool.query(`SELECT status FROM user WHERE id=? AND login_timestamp=?`, [user.id, login_timestamp]);
if(checkToken.length > 0){
if(checkToken[0].status == 0){
res.json({
status: 004,
msg: "Auth failed. Account Disabled."
});
return;
}
next();
}else{
res.json({
status: 003,
msg: "Timestamp does not match."
});
return;
}
});
}catch(e){
// console.log(e);
res.json({
status: 005,
msg: "User auth failed. Internal Server Error."
});
return;
}
}
So to access req.body, I need to first use multer.single("image") as a middleware.
Now if I use multer.single("image") before I use authorizeUser then the image is uploaded first even before checking the auth of the user. And if I use it after authorizeUser then I cannot access req.body in authorizeUser. And if I use this order of middleware: multer.none(), authorizeUser, multer.single("image") then it does not upload the image.
How to solve this issue? How can I authorizeUser first and then upload the image while accessing req.body in authorizeUser?
Here is the code where I want to parse multiform data:
const express = require('express')
const bodyparser = require('body-parser');
const pool = require('./utils/connectionPool')
const multer = require('multer');
const app = express()
app.use(bodyparser.json());
app.use(express.urlencoded({extended: true}))
const port = 3000
// @beyondhelloworld by Femin Dharamshi
const login = require('./routes/login');
const {authorizeUser} = require('./utils/auth');
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/')
},
filename: function (req, file, cb) {
cb(null, Date.now() + file.originalname)
}
})
var upload = multer({ storage: storage })
app.get('/', upload.single('image'), authorizeUser, async (req, res) => {
//This is just for trial.
resp = await pool.query("SELECT * FROM user WHERE id=?",[req.body.id]);
res.json(resp)
})
app.use('/login', login);
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
EDIT: TL;DR I want to access req.body before the file upload takes place.
This is how you use multer to upload the image upon authorization.
Without a frontend to test a file upload, you need to mock the request using whichever API you want. I am using for this example postman.
First, set up your request in postman.
You got to use a POST
request with the endpoint you want to test.
Here is a screnshot:
Note in the body tab you must use form-data
, enter a value for key
(I have used image) and in the dropdown menu next to the value (on the left), use file
. Now you can upload your image.
Second, your code...(Note I have removed a lot of noise and unnecessary code for this test but you will still be able to figure things out anyway):
const express = require('express')
const bodyparser = require('body-parser');
const multer = require('multer');
const upload = multer();
const port = 3000
function authorizeUser(req, res, next) {
// we are setting this variable to false initially
// so that you can run your checks, make DB calls, whatever...
const isAuthorized = false;
// if is not authorised
// the middleware send a not authorised response
if (!isAuthorized) {
return res.send("User Not Authorised");
}
// if the user is authorised we call next
next();
}
const app = express();
app.use(bodyparser.json());
app.use(express.urlencoded({ extended: true }))
// for simplicity I am using just a post endpoint to test things
app.post('/', upload.single('image'), authorizeUser, (req, res) => {
// here you save the image in a variable `file` IF
// only IF the middleware has checked the user in...
const file = req.file;
// and you can now log / use the file...
console.log(file);
// send a response back to the user
// this won't get executed if the middleware return unauthorised
res.send("User authorised");
res.end();
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
Now, once you have made the necessary adjustment to your code, send the request via postman by clicking send.
Since the variable isAuthorised
is false (initial value to mock an unauthorised user), as you can see from the postman GUI, the response returned to the user is: "User Not Authorised".
Check you server logs now; as you can see no file is printed to the console.
Next, change the variable isAuthorised to true
and resend the request via postman (you may need to restart your server...).
See the response: "User authorised".
Check you server logs again and now you can see the file is loaded and can be used for further processing:
{
fieldname: 'image',
originalname: 'myAvatar.png',
encoding: '7bit',
mimetype: 'image/png',
buffer: <Buffer 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 00 c8 00 00 00 c8 08 06 00 00 00 ad 58 ae 9e 00 00 20 00 49 44 41 54 78 9c ec 9d 77 5c 14 e7 d6 ... 27133 more bytes>,
size: 27183
}
Does this help?