JWT-Node authentication - req.headers[''authorization"] is Undefined - node.js

I try to set Token then get token from header but it always shows "undefined" when i console.log(req.headers['authorization'])
Here is some code
const express = require('express');
const app = express();
const jwt = require('jsonwebtoken');
const accessTokenSecret = 'youraccesstokensecret';
app.listen(8080, () => {
console.log('Authentication service started on port 8080');
});
I set token
app.get("/createJWT",function(req,res){
const accessToken = jwt.sign({ username: "myName"}, accessTokenSecret);
res.json({
accessToken
});
})
the middleware (I show req.headers['authorization'] is undefined)
const authenticateJWT = (req, res, next) => {
const authHeader = req.headers['authorization'];
console.log(authHeader)
if (authHeader) {
const token = authHeader.split(' ')[1];
jwt.verify(token, accessTokenSecret, (err, user) => {
if (err) {
return res.sendStatus(403);
}
req.user = user;
next();
});
} else {
res.sendStatus(401);
}
};
And finally, I access a route to test, req.headers['authorization'] does not have values.
app.get("/check",authenticateJWT,function(req, res){
res.send("ok")
})
please help me, thanks

So when you are calling the API form the frontend or say you are checking the API with postman are you setting up the header while requesting /check? (I am not talking about your /createJWT which creates the token and sends it as a response)
In your frontend code/postman you need to explicitly add the header authorization JWTtoken while creating an HTTP request and after that your backend will receive it. Kindly check if this is not being done.

Related

I keep getting the following error "Cannot set headers after they are sent to the client" when i use headers.authorization in jwt

I'm working on a project and i can't seem to able to make jwt work, everytime i try to use authorization in my header and provide a valid token, my server crashes and it gives me the error in the title.
Here's my middleware for jwt :
const jwt = require("jsonwebtoken");
const config = process.env;
const verifyToken = (req, res, next) => {
const token = req.headers["authorization"];
// check if there's no token in the request
if (!token) {
return res.status(403).send("A token is required for authentication");
}
// Verify the JWT and decode the user ID
try {
const decoded = jwt.verify(token, config.TOKEN_KEY);
// check if the token has expired
if (decoded.exp < Date.now() / 1000) {
return res.status(401).send({ message: 'Token has expired.' });
}
req.user = decoded;
} catch (err) {
res.status(400).send({ message: 'Invalid token.' });
}
return next();
}
module.exports = verifyToken;
An example of one of the routes i try to access using an authenticated user's token :
const express = require("express");
const router = express.Router();
const categoryController = require('../controllers/categoryController');
const verifyToken = require('../middleware/auth');
router.get("/get_category", verifyToken,categoryController.get_category);
router.post("/create_category", categoryController.create_category);
router.post("/update_category", categoryController.update_category);
router.get("/delete_category/:_id", categoryController.delete_category);
module.exports = router;

Authenication with JWT returning empty array with a GET request [NodeJS]

Trying to practice JWT with NodeJS but when I try to run a GET request using the headers of the Authorization: Bearer , it returns an array instead of the object with the token, I tried with the Postman app but to no avail
This is the code, the get and post request code:
const express = require('express');
require('dotenv').config();
const jwt = require('jsonwebtoken');
const crypto = require('crypto');
const app = express();
const user = [
{
username: 'John',
email: 'john#gmail.com'
},
{
username: "Joshua",
email: 'joshua#gmail.com'
}
]
app.use(express.json())
app.get('/api', authenicateToken, (req, res,) => {
res.json(user.filter(post => post.username === req.body.name))
})
app.post('/login', (req, res) => {
const username = req.body.username
const use = { name: username }
const accessToken = jwt.sign(use, process.env.ACCESS_KEY)
res.json({ accessToken: accessToken })
})
function authenicateToken (req, res, next) {
const authHeader = req.headers['authorization']
const token = authHeader && authHeader.split(' ')[1]
if(token === null) return res.sendStatus(403)
jwt.verify(token, process.env.ACCESS_KEY, (err, user) => {
if(err) return res.sendStatus(403)
req.user = user
next()
})
}
app.listen(4000, () => {
console.log('Can you hear me?');
})
filter returns an array, use find method it returns single item.
res.json(user.find(post => post.username === req.body.name))

How to share token between different routes in node js?

In the user.js file, I created the token here with this code
if (user && bcrypt.compareSync(req.body.password, user.passwordHash)) {
const token = jwt.sign(
{
userId: user.id,
isAdmin: user.isAdmin,
},
process.env.SECRET,
{
expiresIn: "50d", // >> on day
}
);
And the token work and everything is ok, But I want to use the token somewhere else, for example, here in cupon.js file
router.post("/cupon", async (req, res) => {
...
const token = req.header("authorization").substring(7);
...
I used this
const token = req.header("authorization").substring(7);
to get the token from the header, Is there a better way to get the token?
You can create a separate middleware for authentication and authorisation. It will help you to reuse it any where or in multiple routes, you can use the same auth middleware and if every thing goes good in auth, you can call next else send the response with 401 status code.
In Auth.js
const jwt = require('jsonwebtoken');
module.exports = (req, res, next) => {
try {
const token = req.headers.authorization.split(' ')[1];
const decodedToken = jwt.verify(token, 'RANDOM_TOKEN_SECRET');
const userId = decodedToken.userId;
if (req.body.userId && req.body.userId !== userId) {
throw 'Invalid user ID';
} else {
next();
}
} catch {
res.status(401).json({
error: new Error('Invalid request!')
});
}
};
Import this Auth.js and pass it to your routes which needs it. This way you can unit test your auth layer and can re-use it anywhere in the code. The below code is for sample purpose:
const express = require('express');
const router = express.Router();
const auth = require('../middleware/auth');
const stuffCtrl = require('../controllers/stuff');
router.get('/', auth, stuffCtrl.getAllStuff);
router.post('/', auth, stuffCtrl.createThing);
router.get('/:id', auth, stuffCtrl.getOneThing);
router.put('/:id', auth, stuffCtrl.modifyThing);
router.delete('/:id', auth, stuffCtrl.deleteThing);
module.exports = router;
For more details, you can check this link and follow along.

How to send jwt token to protected route in node.js

I have created a login form that should redirect the user to a dashboard page in case he enters the right password and username. If the user tries to navigate to the dashboard url without being logged in the page should not display as it is a protected route. I am trying to send a jwt token when the user logs in, but that doesn't work I just get the Forbidden message when I log in so it seems that the token is not sent correctly, how can I send the jwt token and access the protected route once the user logs in successfully?
Here is my server.js:
const express = require('express');
const jwt = require('jsonwebtoken');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
let Post = require('./models/post.model.js');
const app = express();
const cors = require('cors');
require('dotenv').config();
app.use(cors());
app.use("/assets", express.static(__dirname + "/assets"));
app.use(bodyParser.urlencoded({ extended: true }));
const BASE_URL = process.env.BASE_URL;
const PORT = process.env.PORT || 1337;
mongoose.connect(BASE_URL, { useNewUrlParser: true, useUnifiedTopology: true })
const connection = mongoose.connection;
connection.once('open', function () {
console.log('Connection to MongoDB established succesfully!');
});
app.set('view-engine', 'ejs');
app.get('/', (req, res) => {
res.render('index.ejs');
});
app.post('/', (req, res) => {
let username = req.body.username;
let password = req.body.password;
const user = {
username: username,
password: password
}
jwt.sign({ user }, process.env.SECRET_KEY, (err, token) => {
res.json({
token
})
});
if (username !== process.env.USER_NAME && password !== process.env.USER_PASSWORD) {
res.json('Invalid credentials');
} else {
res.setHeader('Authorization', 'Bearer '+ token);
res.redirect('/dashboard')
}
});
app.get('/dashboard', verifyToken, (req, res) => {
jwt.verify(req.token, process.env.SECRET_KEY, (err, authData) => {
if (err) {
res.sendStatus(403);
} else {
res.sendStatus(200);
}
});
res.render('dashboard.ejs');
});
app.get('/dashboard/createPost', verifyToken, (req, res) => {
res.render('post.ejs');
});
app.post('/dashboard/createPost', async (req, res) => {
let collection = connection.collection(process.env.POSTS_WITH_TAGS);
res.setHeader('Content-Type', 'application/json');
let post = new Post(req.body);
collection.insertOne(post)
.then(post => {
res.redirect('/dashboard')
})
.catch(err => {
res.status(400).send(err);
});
});
// TOKEN FORMAT
// Authorization: Bearer <access_token>
//Verifing the Token
function verifyToken(req, res, next) {
// Get auth header value
const bearerHeader = req.headers['authorization'];
// Check if bearer is undefined
if (typeof bearerHeader !== 'undefined') {
// Spliting the bearer
const bearer = bearerHeader.split(' ');
// Get token from array
const bearerToken = bearer[1];
// Set the token
req.token = bearerToken;
// Next middleware
next();
} else {
// Forbid the route
res.sendStatus(403);
}
}
app.listen(PORT);
see this example, i use middleware(checkAuthLogin), this code contains all thing for your question:
index.js:
const express = require('express');
const app = express();
require('./db/mongoose');
const userRouter = require('./routers/user');
app.use(express.json());
app.use(userRouter);
app.listen(3000, ()=> {
console.log('Server is up on port ', 3000)
});
db/mongoose.js:
const mongoose = require('mongoose');
mongoose.connect("mongodb://127.0.0.1:27017/db-test" {
useNewUrlParser : true,
useCreateIndex : true,
useFindAndModify : false,
useUnifiedTopology: true
});
routers/user.js:
const express = require('express');
const router = new express.Router();
const RootUser = require('../models/root-user');
const {checkRootLogin} = require('../middleware/checkAuthLogin');
router.post('/createrootuser', async (req, res) => {
const updates = Object.keys(req.body);
const allowedUpdatesArray = ['name', 'password'];
const isValidOperation = updates.every((update) => allowedUpdatesArray.includes(update));
if (!isValidOperation) {
return res.status(400).send({error: 'Invalid Request Body'})
}
const rootUser = new RootUser(req.body);
try {
await rootUser.save();
// sendWelcomeEmail(user.email, user.name)
const token = await rootUser.generateAuthToken();
//console.log(user)
res.status(201).send({rootUser, token});
} catch (e) {
res.status(400).send(e)
}
});
//use this middleware(checkRootLogin) for check root user can access this function
router.post('/rootconfig', checkRootLogin, async (req, res) => {
res.status(200).send({success: 'success add root config'})
});
module.exports = router;
model/root-user.js:
const mongoose = require('mongoose');
const validator = require('validator');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const userRootSchema = new mongoose.Schema({
name: {
type : String,
required: true,
unique : true,
trim : true,
lowercase : true,
},
password: {
type : String,
required: true,
unique : true,
trim : true,
lowercase : true,
minlength : 6,
validate (value) {
//if (validator.contains(value.toLowerCase(), 'password')){
if (value.toLowerCase().includes('password')){
throw new Error('Password can not contained "password"')
}
}
},
tokens : [{
token : {
type : String ,
required : true
}
}],
}, {
timestamps: true
});
userRootSchema.methods.generateAuthToken = async function(){
const root = this;
// generate token
try {
// const token = jwt.sign({ _id : user._id.toString()}, process.env.JWT_SECRET);
const token = jwt.sign({ _id : root._id.toString()}, "test");
// add token to user model
root.tokens = root.tokens.concat({ token });
await root.save();
return token
} catch (e){
throw new Error(e)
}
};
userRootSchema.pre('save', async function(next){
// this give ccess to individual user
const user = this;
if (user.isModified('password')){
user.password = await bcrypt.hash(user.password, 8)
}
next()
});
const UserRoot = mongoose.model('UserRoot', userRootSchema);
module.exports = UserRoot;
middleware/checkAuthLogin.js:
const jwt = require('jsonwebtoken');
const RootUser = require('../models/root-user');
const checkRootLogin = async (req, res, next) => {
try {
const token = req.header('Authorization').replace('Bearer ', '');
// const decoded = jwt.verify(token, process.env.JWT_SECRET);
const decoded = jwt.verify(token, "test");
const rootUser = await RootUser.findOne({_id: decoded._id, 'tokens.token': token});
if (!rootUser) {
throw new Error("User cannot find!!");
}
req.token = token;
req.rootUser = rootUser;
req.userID = rootUser._id;
next()
} catch (e) {
res.status(401).send({error: 'Authentication problem!!'})
}
};
module.exports = {checkRootLogin};
Your issue is that your token variable is only accessible inside of the callback to the jwt.sign call, so when you try to do this here res.setHeader('Authorization', 'Bearer '+ token);, it won't know what variable you're referring to, hence the undefined error. By the way, if you're going to use jwt.sign asynchronously, then the code that uses it needs to also be inside of the callback, otherwise synchronous code outside of the callback will likely execute first (and thus not be able to access any results of the asynchronous code) as the asynchronous callback executes in the background. The solution here is to either switch your usage to a synchronous usage or place your response code inside of the callback. Also, calling res.json will end the response so I'm not sure what exactly you're trying to accomplish with the multiple response calls
Synchronous version:
app.post('/', (req, res) => {
let username = req.body.username;
let password = req.body.password;
const user = {
username: username,
password: password
};
let token = undefined;
try {
token = jwt.sign({ user }, process.env.SECRET_KEY);
} catch (e) {
// handle error
}
if (username !== process.env.USER_NAME && password !== process.env.USER_PASSWORD) {
res.json('Invalid credentials');
} else {
res.setHeader('Authorization', 'Bearer '+ token);
res.redirect('/dashboard');
}
});
Asynchronous version:
app.post('/', (req, res) => {
let username = req.body.username;
let password = req.body.password;
const user = {
username: username,
password: password
}
jwt.sign({ user }, process.env.SECRET_KEY, (err, token) => {
if (username !== process.env.USER_NAME && password !== process.env.USER_PASSWORD) {
res.json('Invalid credentials');
} else {
res.setHeader('Authorization', 'Bearer '+ token);
res.redirect('/dashboard')
}
});
});
In these examples, I took out res.json({ token }) because you can't use res.json and then perform a redirect, but modify those parts however best fits your code. On another note, you probably don't want to include the password in your token because while JWTs (when using the default/standard algorithms which do not include encryption) are cryptographically guaranteed to be unmodifiable, they are still readable
I have one solution to send jwt token, but you will need to install one more package. If you think it worth maybe you can follow.
I use express only for backend api. But you can use the same logic applied here to your application.
The lib you will need to install is the express-jwt
It handles routes to block access to endpoint that need authentication.
server.js
require('dotenv').config()
const express = require('express');
const logger = require('morgan');
const cors = require('cors');
const jwt = require('jsonwebtoken');
const expressJwt = require('express-jwt');
const app = express();
cors({ credentials: true, origin: true });
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use('/secure', expressJwt({ secret: process.env.SECRET }));
app.use(require('./server/index'));
app.get('/secure/dashboard') => {
//now you can only access this route with authorization header
//prependending the '/secure/ to new routes should make them return 401 when accessed without authorization token
//accessing this route without returns 401.
//there is no need to validate because express-jwt is handling.
console.log(res.user)//should print current user and pass signed with token
res.render('dashboard.ejs');
});
app.post('/', (req, res) => {
let username = req.body.username;
let password = req.body.password;
//jwt.sign({ user }, process.env.SECRET_KEY, (err, token) => {
// res.json({
// token
// })
//});
//shouldn't sign json here, because there is no guarantee this is a valid
//username and password it can be an impostor
if (username !== process.env.USER_NAME && password !== process.env.USER_PASSWORD) {
res.json('Invalid credentials');
} else {
const user = {
username: username,
password: password
};
const tk = {};
tk.token = 'Bearer ' + jwt.sign(user, process.env.SECRET_KEY, { expiresIn: 1800 });//expires in 1800 seconds
res.status(200).json(tk);
}
});
Now in your frontend put the authorization token sent by this route in cookies or store in client-side.
Do the next request with the header authorization for the secure dashboard route.
I think the problem in the sign in controller function
you must check first if the user have the correct password before attempting to send him a token
you should save the result of jwt sign function in a variable to send back to the user in case he has the right credintials.
It make no sense to send the password again to the user , only the username is needed
you can try this :
app.post('/', (req, res) => {
const {username , password} = req.body;
if (username !== process.env.USER_NAME && password !== process.env.USER_PASSWORD) {
return res.json('Invalid credentials');
}
const token = jwt.sign({username:username }, SECRET)
res.setHeader('Authorization', token);
res.redirect('/dashboard')
});

Why nodejs returns 404 even after processing the request for the route?

I am using jwtwebtoken, express, mongo, nodejs, bcrypt for my backend APIs. I notices that the router that i have for localhost:3000/account/update path is getting executed but it returns 404 after successful operation. Db connection is fine. but the localhost:3000/account/update route in postman gets 404 Why?
server.js
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const dbUtils = require('./services/dbconnect');
module.exports = app;
// get an instance of the router for account api routes.
const accountRoutes = require('./services/account');
const registerRoutes = require('./services/register');
const authenticateRoutes = require('./services/authentication');
// used to create, sign, and verify tokens
const jwt = require('jsonwebtoken');
// Secret key to generate new tockens.
app.set('superSecret', "11111"); // secret variable
// parse application/json
app.use(bodyParser.json())
app.use('/account', accountRoutes);
app.use('/authenticate', authenticateRoutes);
app.use('/register', registerRoutes);
app.get('/', (req, res) => res.send('Hello World!'));
dbUtils.connectToServer(() =>{
app.listen(3000, () => console.log('Server listening on port 3000!'));
});
each-request.js
const jwt = require('jsonwebtoken'); // used to create, sign, and verify tokens
const app = require('./../server');
function verifyLoginTokenForEachRequest(req, res, next) {
// check header parameters for token
const token = req.headers['x-access-token'];
try {
if (token) {
// verifies secret and checks exp
jwt.verify(token, app.get('superSecret'), (err, decoded) => {
console.log(decoded);
if (err) {
throw err || new Error('not authorized')
} else {
// if everything is good, save to request for use in other routes
req.decoded = decoded;
console.log("[authorized] user logged in")
next();
}
});
} else {
// if there is no token, return an error
return res.status(403).send({msg: "not authorized"});
}
} catch(e) {
console.log("[unauthorized]", e);
return res.status(401).json({ msg: 'Failed to authenticate token.' });
} finally{
next();
}
}
module.exports={verifyLoginTokenForEachRequest};
account.js
const express = require('express')
const router = express.Router();
const verifyLoginTokenForEachRequest = require('./each-request').verifyLoginTokenForEachRequest;
const dbUtils = require('./dbconnect');
router.use(verifyLoginTokenForEachRequest);
router.post('/update', (req, res) => {
const body = req.body;
const db = dbUtils.getDb();
console.log(body);
try {
db.collection('users')
.updateOne({emailid: body.emailid},{$set:{firstName: body.firstName, lastName: body.lastName, lastModified: body.lastModified}},function(err, doc){
if(err || !doc) {
console.log(err);
throw err || new Error('Failed to update')
} else {
console.log("[success] update success");
console.log(doc)
res.status(200).send();
}
});
} catch(e) {
console.error("[error] failed to update");
res.status(500).send({msg: 'failed to update'});
}
});
module.exports = router;
There are a bunch of issues I see wrong here. I'm not sure which ones exactly do or don't add up to exactly what you asked about, but you need to first clean up these issues and then see what problems are left (if any).
Your verifyLoginTokenForEachRequest() function is always calling next(), even when it's already sent a 401 or 403 response. If you send a response, don't call next(). next() tells Express to keep going to the next routes. Once you've sent a response, you're done. Stop further routing. Don't call next().
In verifyLoginTokenForEachRequest(), you are trying to create an error condition by doing a throw xxx inside an async callback and then expecting to catch that at a higher level. That will not work. The exception will just go back into the bowels of jwt.verify() and will not get to your exception handler. Async exceptions (unless they are part of a promise .then() handler) will go nowhere. You can't throw and catch them at a higher level. You have to deal with them where they occur. In this case, you should just send your error response right there.
In verifyLoginTokenForEachRequest(), you're calling next() in a finally clause when you very well may have already sent the response. Only call next() when you want routing to continue to other route handlers and you have not yet sent a response.
In router.post('/update', ...), you're again throwing an exception inside an async callback which will not be caught by your exception handler. Can't do that. Send the error response there or use promises so you can more easily propagate the error to a higher level.
Here's a fixed up version of verifyLoginTokenForEachRequest():
const jwt = require('jsonwebtoken'); // used to create, sign, and verify tokens
const app = require('./../server');
function verifyLoginTokenForEachRequest(req, res, next) {
// check header parameters for token
const token = req.headers['x-access-token'];
if (token) {
// verifies secret and checks exp
jwt.verify(token, app.get('superSecret'), (err, decoded) => {
console.log(decoded);
if (err) {
console.log("[verifyLoginTokenForEachRequest]", err);
return res.status(401).json({msg: 'Failed to authenticate token.'});
} else {
// if everything is good, save to request for use in other routes
req.decoded = decoded;
console.log("[authorized] user logged in")
next();
}
});
} else {
// if there is no token, return an error
return res.status(403).send({msg: "not authorized"});
}
}
module.exports = {
verifyLoginTokenForEachRequest
};
Here's a fixed up version of account.js:
const express = require('express')
const router = express.Router();
const verifyLoginTokenForEachRequest = require('./each-request').verifyLoginTokenForEachRequest;
const dbUtils = require('./dbconnect');
router.use(verifyLoginTokenForEachRequest);
router.post('/update', (req, res) => {
const body = req.body;
const db = dbUtils.getDb();
console.log(body);
db.collection('users')
.updateOne({emailid: body.emailid},{$set:{firstName: body.firstName, lastName: body.lastName, lastModified: body.lastModified}},function(err, doc){
if(err || !doc) {
console.error("[error] failed to update", err);
res.status(500).send({msg: 'failed to update'});
} else {
console.log("[success] update success");
console.log(doc);
res.end();
}
});
});
module.exports = router;

Resources