I set up jsonwebtoken in node.js and testing it with postman. First upon login I generate the token, then I copy it and use for the post request to a protected route. And jwt.verify runs just fine, the correct userId is retrieved, but when I call next() (see below) it goes to 404 error handler instead of the index where the protected routes are located:
var index = require('./routes/index')
function verifyToken (req, res, next) {
// Get auth header value
const bearerHeader = req.headers['authorization']
// check if bearer is undefined
const message = 'Unauthorized user, access denied!'
if (typeof bearerHeader !== 'undefined') {
const bearerToken = bearerHeader.split(' ')[1]
jwt.verify(bearerToken, 'privateKey', (err, userData) => {
if (err) {
// Forbidden
console.log(err)
res.status(403).send({ err })
} else {
console.log(userData.userId)
req.userId = userData.userId
------> next()
}
})
} else {
// Forbidden
res.status(403).send({ message })
// res.sendStatus(403)
}
}
// app.use('/auth', index);
app.use('/auth', verifyToken, index)
Why is it happening? What am I doing wrong here? Basically my goal is to set userId on the req object, then get it back in the root route auth/ in index
Related
const express = require("express");
const jwt = require("jsonwebtoken");
const { type } = require("os");
const app = express();
app.get("/api", (req,res) => {
res.json({
message: "Welcome to the API"
});
});
app.post("/api/posts", (req,res) => {
res.json({
message: "Post has been created.."
});
});
app.post("/api/login", verifyToken, (req,res) => {
// Mock User
const user = {
id: 24,
username: "Jonah",
email: "jonah_52#gmail.com"
}
jwt.sign({user}, "secretkey", (err, token) => {
res.json({
token
});
});
});
// Verify Token
function verifyToken(req,res,next) {
// Get authorization header value
const bearerHeader = req.headers["authorization"];
}
// Check if bearer is undefined
if(typeof bearerHeader !== "undefined") {
}else{
// Forbidden
res.sendStatus(403);
}
app.listen(5000, () => console.log("Server is listening on port 5000.."));
I want to make Node.js API Authentication With JWT and when I check this with if/else method I get an error that is saying "res is not defined", how can I defined this "res" and fix the error?
res.sendStatus(403);
^
ReferenceError: res is not defined
You have some improper bracing in your verifyToken() function. Change this:
// Verify Token
function verifyToken(req,res,next) {
// Get authorization header value
const bearerHeader = req.headers["authorization"];
}
// Check if bearer is undefined
if(typeof bearerHeader !== "undefined") {
}else{
// Forbidden
res.sendStatus(403);
}
to this:
// Verify Token
function verifyToken(req,res,next) {
// Get authorization header value
const bearerHeader = req.headers["authorization"];
// Check if bearer is undefined
// FIX THIS: need to not just check if it's undefined, but if
// its valid
if(typeof bearerHeader !== "undefined") {
next();
} else {
// Forbidden
res.sendStatus(403);
}
}
I have an auth protect middleware that checks if req.params.id === req.userId(the one returned by bcrypt verify function). I have a protect function which upon bcrypt.verify returns the decoded.id.
The Id returned from req.user._id despite being the same as decoded.id returns "not authorized in the verifyandAuth middleware, however, if I replace req.user._id by decoded.id(in verifyandAuth), the if function works and the middleware goes through without giving the "not authorized error". Can anybody please tell me why that's happening? (req.user._id and decoded.id upon console.log show the same id, as such, there's no mistake there).
Protect Middleware
export const protect = async (req, res, next) => {
let token;
if (
req.headers.authorization &&
req.headers.authorization.startsWith("Bearer")
)
try {
{
token = req.headers.authorization.split(" ")[1];
const decoded = jwt.verify(token, "kris");
req.userId = decoded.id;
req.user = await User.findById(decoded.id).select("-password");
next();
}
} catch (error) {
res.status(400).json(error.message);
}
if (!token) {
return res.status(400).json("Invalid Token");
}
};
auth Middleware
export const verifyandAuth = (req, res, next) => {
protect(req, res, () => {
console.log(req.user._id, req.params.id);
if (req.user._id === req.params.id || req.isAdmin) {
next();
} else {
res.status(400).json("Not authorised");
}
});
};
I'm trying to parse a URL in an Express backend but when I go to a url like localhost:3000/token=myJwtToken I get undefined for req.query.token in my authJwt.verifyToken middleware in the backend.
In my React App.js, I'm hitting the below endpoint where I need to verify a token in the URL as a param / query string with a secret shared by sending and receiving parties:
const req = {
url: 'http://localhost:8080/api/verify',
method: 'GET',
};
In my backend I have this route:
module.exports = function (app) {
app.get(
'/api/verify',
[authJwt.verifyToken], // verify token in URL middleware
galleryController.loadGallery, // proceed to gallery
);
};
My verifyToken auth middleware is thus:
verifyToken = (req, res, next) => {
let token = req.query.token;
console.log(token); // undefined
console.log(req.params); // {}
if (!token) {
console.log('NO TOKEN!!!');
return res.status(401).send({
message: 'Unauthorized!',
});
}
jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
if (err) {
console.log(`Error: ${err}`);
return res.status(401).send({
message: `Error: ${err}`,
});
}
console.log('success!', decoded.token);
next();
});
};
Where have I gone wrong?
localhost:3000/token=myJwtToken is an (invalid) path, not a query parameter
localhost:3000/api/verify?token=myJwtToken will return a query parameter with key token and value myJwtToken
I am trying to set up viewpages to show the authenticated user's info such as user's name or email on the page when they are logged in.
To do so, I am using the res.locals function to set the user data at the global level for the pages to access.
const jwt = require("jsonwebtoken")
const User = require("../models/User")
const checkUser = (req, res, next) => {
const token = req.cookies.jwt
if (token) {
jwt.verify(token, "Company_Special_Code", async (err, decodedToken) => {
if (err) {
res.locals.user = null // Set it to null if the user does not exist
next();
} else {
let user = await User.findById(decodedToken.id)
res.locals.user = user
next();
}
})
} else {
res.locals.user = null
next();
}
}
module.exports = {
checkUser
}
The first code, where I call next() function every time the code reaches an endpoint, allows the pages to access the user info without any errors.
However, if I call the next() function only once at the very bottom of the checkUser() function, it causes error claiming that the user is not defined at the view page level. The code is as follows:
const jwt = require("jsonwebtoken")
const User = require("../models/User")
const checkUser = (req, res, next) => {
const token = req.cookies.jwt
if (token) {
jwt.verify(token, "Company_Special_Code", async (err, decodedToken) => {
if (err) {
res.locals.user = null // Set it to null if the user does not exist
} else {
let user = await User.findById(decodedToken.id)
res.locals.user = user
}
})
} else {
res.locals.user = null
}
next();
}
module.exports = {
checkUser
}
If I coded the function correctly, the checkUser() function should get to the next() function at the bottom regardless of the status of jwt token or if there was an error during the token verification process. I would really appreciate your help if you can tell me what I am getting it wrong here...
Your jwt.verify is has an asynchronous callback and next() at the bottom is being called before that returns. So you either need to put next() into that callback, or use jsonwebtoken synchronously. Something like this:
const checkUser = (req, res, next) => {
const token = req.cookies.jwt
if (token) {
try {
const decodedToken = jwt.verify(token, "Company_Special_Code")
// This only runs if the token was decoded successfully
let user = await User.findById(decodedToken.id)
res.locals.user = user
} catch (error) {
res.locals.user = null // Set it to null if the user does not exist
}
} else {
res.locals.user = null
}
next();
}
When you use an async callback like that, javascript will continue processing the rest of the script while that callback is running on the side (more or less). So next() is being called unaware of the need to wait for the callback or anything it might handle.
Your validation part misses the correct error handling in middleware. If token is invalid, then why should user get access to controller, you can send error from middleware itself. If you are not sending error from middleware and calling next(), then will defeat purpose of your authentication middleware.
Update your code as follows,
const jwt = require("jsonwebtoken")
const User = require("../models/User")
// The routes, which does not requires aurthentication
const usecuredRoutes = []
const checkUser = (req, res, next) => {
if(usecuredRoutes.indexOf(req.path) === -1){
const token = req.cookies.jwt
if (token) {
jwt.verify(token, "Company_Special_Code", async (err, decodedToken) => {
if (err) {
res.locals.user = null // Set it to null if the user does not exist
// Token is invalid, then telll user that he is don't have access to the resource
res.status(403).send('Unauthorized')
} else {
let user = await User.findById(decodedToken.id)
res.locals.user = user
next();
}
})
} else {
// Token does not exists, then telll user that he is don't have access to the resource
res.status(403).send('Unauthorized')
}
} else {
next()
}
}
module.exports = {
checkUser
}
After I added a middleware for authorization and roles purposes I cannot get data from protected endpoint.
this route is responsible for fetching all coming visits
router.get('/all', authorize, VisitController.getAllVisits);
authorize is a middleware responsible for checking wether user is logged in and has proper role(s):
module.exports.authorize = (req, res, next) => {
roles = ['super_admin']
if (typeof roles === 'string') {
roles = [roles];
}
verifyToken(req, res, next),
const role_ = req.get('Role')
if (roles.length && !roles.includes(role_)) {
return res.status(401).json({ message: 'Unauthorized' });
}
next();
}
and when I call Get on this endpoint with super_admin role via Postman first I got this error (404 not found):
Cannot GET /api/visits/all
and on server side I got:
(node:5920) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:470:11)
at ServerResponse.header (C:\Users\jan\Desktop\hairdresser-service\service-api\node_modules\express\lib\response.js:767:10)
at ServerResponse.send (C:\Users\jan\Desktop\hairdresser-service\service-api\node_modules\express\lib\response.js:170:12)
at ServerResponse.json (C:\Users\jan\Desktop\hairdresser-service\service-api\node_modules\express\lib\response.js:267:15)
at Visit.find.then.documents (C:\Users\jan\Desktop\hairdresser-service\service-api\controllers\visitController.js:127:28)
at process._tickCallback (internal/process/next_tick.js:68:7)
getAllVisits function:
module.exports.getAllVisits = (req, res) => {
Visit.find().then(documents => {
return res.json({
message: 'Visits fetched successfully',
visits: documents
});
});
};
verifyToken function:
module.exports.verifyToken = (req, res, next) => {
const bearerHeader = req.headers['authorization']
if (typeof bearerHeader !== 'undefined') {
const bearer = bearerHeader.split(' ')
const bearerToken = bearer[1]
if(bearerToken == 'undefined' || bearerToken == '') {
res.json({
status: 403
})
}
req.token = bearerToken
next()
} else {
res.json({
status: 403
})
}
}
So what is the problem here? Some say it's because of sending many responses, but I don't do that.
ERR_HTTP_HEADERS_SENT is telling you that you're ending the request twice. That's happening because in module.exports.authorize you're calling verifyToken and ignoring that it's ending the request or calling next.
verifyToken should be separated from authorize. An easy fix would be to export an array of middlewares
module.exports.authorize = [
verifyToken,
(req, res, next) => {
roles = ['super_admin']
if (typeof roles === 'string') {
roles = [roles];
}
const role_ = req.get('Role')
if (roles.length && !roles.includes(role_)) {
return res.status(401).json({ message: 'Unauthorized' });
}
next();
}
];
Now your code will call first, verifyToken, and it next is called, it will go to the middleware that's checking the roles.