I am trying to protect application routes using jwt.I am able to generate jwt and to validate jwt I have created middleware authorize.js which I am passing to /sec route in below code but when I am trying to access protected routes using jwt it showing below error:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:518:11)
at ServerResponse.header (D:\Backend\NodeJs\aws_test\node_modules\express\lib\response.js:771:10)
at ServerResponse.send (D:\Backend\NodeJs\aws_test\node_modules\express\lib\response.js:170:12)
at ServerResponse.json (D:\Backend\NodeJs\aws_test\node_modules\express\lib\response.js:267:15)
at D:\Backend\NodeJs\aws_test\server.js:24:9
at Layer.handle [as handle_request] (D:\Backend\NodeJs\aws_test\node_modules\express\lib\router\layer.js:95:5)
at next (D:\Backend\NodeJs\aws_test\node_modules\express\lib\router\route.js:137:13)
at D:\Backend\NodeJs\aws_test\auth.js:21:20
at Object.module.exports [as verify] (D:\Backend\NodeJs\aws_test\node_modules\jsonwebtoken\verify.js:53:12)
at authorize (D:\Backend\NodeJs\aws_test\auth.js:11:13)
Below is how I am setting jwt in POSTMAN:
Below is my code:
server.js
const express = require('express');
const jwt = require('jsonwebtoken');
const authorize = require('./auth');
const chalk = require('chalk');
const app = express();
const port = 3000 || process.env.PORT;
app.get('/',(req,res) => {
res.send("Home page");
});
app.get('/jwt',(req,res) => {
let token = jwt.sign({"body":"stuff"},"mypassphrase",{ algorithm: 'HS256'});
console.log(chalk.blue(token));
});
app.get('/sec',authorize,(req,res) => {
res.json({"name":"Hello digi"});
});
app.listen(port,(req,res) => {
console.log(chalk.green(`App is running at ${port}`));
});
auth.js
const fs = require('fs');
const jwt = require('jsonwebtoken');
authorize = (req,res,next) => {
if(typeof req.headers.authorization !== "undefined"){
let token = req.headers.authorization.split(" ")[1];
let key = fs.readFileSync('./private.pem','utf-8');
jwt.verify(token, key,{ algorithm: "HS256" },(err,user) => {
if (err) {
// shut them out!
res.json({ error: "Not Authorized" });
// throw new Error("Not Authorized");
}
// if the JWT is valid, allow them to hit
// the intended endpoint
return next();
});
}
else{
// No authorization header exists on the incoming
// request, return not authorized and throw a new error
res.json({ error: "No Authorization header" });
// throw new Error("Not Authorized");
}
}
module.exports = authorize;
What I am doing wrong in above code or what need to be correct.
The only execution path that looks to be liable to cause the error you're seeing is the following - in the autorize middleware:
if (err) {
res.json({ error: "Not Authorized" }); // you're not returning here
}
return next();
In case of an error happening here, you're not returning execution to the calling code when sending back a response to the client, so the current request is fowarded to the next middelware in line - which will end up resulting in another response being sent.
As for the reason why this path is being executed in the first place, judging from your Postman screen grab, the value you're setting for the Authorization header looks it may be incorrect - you're setting the value to jwt, and the description to the actual token, while you would actually want to set the value to the combination of both separated by a space like jwt token.
Related
< Here what error I am getting on vs code
Cannot set headers after they are sent to the client
node:internal/errors:465
ErrorCaptureStackTrace(err);
^
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at new NodeError (node:internal/errors:372:5)
at ServerResponse.setHeader (node:_http_outgoing:576:11)
at ServerResponse.header (D:\Jwt\server\node_modules\express\lib\response.js:794:10)
at ServerResponse.send (D:\Jwt\server\node_modules\express\lib\response.js:174:12)
at D:\Jwt\server\routes\jwtAuth.js:31:21
at processTicksAndRejections (node:internal/process/task_queues:96:5) {
code: 'ERR_HTTP_HEADERS_SENT'
} >
<` Here is my routes/jwtAuth.js file where I put all restful api and token api to generate
token in postman but I do not know why its just adding new user into the database while it
has to show the token in the postmand
const router =require("express").Router();
const pool = require("../db");
const bcrypt = require("bcrypt");
const jwtGenerator = require("../utils/jwtGenerator");
router.post("/register",async(req,res) => {
try{
const {name,email,password } = req.body;
const user =await pool.query("SELECT * FROM users WHERE user_email =$1", [email]);
if(user.rows.length != 0){
return res.status(401).send("User already exist");
}
const saltRound =10;
const salt = await bcrypt.genSalt(saltRound);
const bcryptPassword = await bcrypt.hash(password,salt);
const newUser = await pool.query("INSERT INTO users (user_name,user_email,user_password)
VALUES($1,$2,$3) RETURNING *",[name,email,bcryptPassword]);
res.json(newUser.rows[0]);
const token = jwtGenerator(newUser.rows[0].user_id);
res.json({ token });
} catch(err){
console.error(err.message);
res.status(500).send("Server error");
}
})
module.exports = router;` >
< `Here is jwtAuth.js files to generate the token maybe something wrong over here that's its not
send head things and error is poping up and that's why token is not appearing in the postman
const jwt = require("jsonwebtoken");
require('dotenv').config();
function jwtGenerator(user_id){
const payload ={
user: user_id
}
return jwt.sign(payload, process.env.jwtSecret, {expiresIn: "1hr"});
}
module.exports = jwtGenerator; `>
< ` Here is my server.js file where server is not running its running i think fine only problem
is in other 2 files someting missing or something parameter is not going through
const express =require("express");
const app = express();
const cors = require("cors");
app.use(express.json());
app.use(cors());
app.use("/auth",require("./routes/jwtAuth"));
app.listen(5000, () => {
console.log("Server is runnong on port 5000 ");
}) ` >
Cannot set headers after they are sent to the client
I remember getting this error when I did multiple response on the same api controller. Try checking that.
I'm having trouble getting the token once the user has logged in. I am supposed to get the token that is generated when I login through req.header so that when I ask for a route in the api (thunder api client) it should be able to give me access
My auth middleware:
const jwt = require('jsonwebtoken');
module.exports = (req, res, next) => {
const token = req.headers['auth-token']
console.log(token)
res.json({
token: token
})
}
My routes:
const { Router } = require('express');
const router = Router();
const {
getAllUsers,
signupUser,
signinUser
} = require('../controllers/user.controller');
const auth = require('../middleware/auth');
router.get('/users', auth, getAllUsers);
router.post('/users/signup', signupUser);
router.post('/users/signin', signinUser);
module.exports = router;
when I user users/signin the response header:
Well well well, Apparently my problem was the following:
When I consumed by (thunder client) the route /users/signin -> it returned the object with the token. What I don't realize is that when I request the route that I want to be restricted to logged in users (only with the token) I must pass the same token that I get back from the signin in the headers of the new request (XD !) as seen in the photo.
Im a beginner at using Node js. Ive developed a website with server fetching using PHP and was trying out something new. So can anyone tell me what Im doing wring here?
var mysql = require('mysql');
var express = require('express');
var session = require('express-session');
var bodyParser = require('body-parser');
var path = require('path');
var connection = mysql.createConnection({
host : 'localhost',
user : 'username',
password: 'password',
database: 'nodelogin'
});
var app = express();
app.use(session({
secret: 'secret',
resave: true,
saveUninitialized: true
}));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.get('/', (request, response) => {
response.sendFile(path.join(__dirname + '/login.html'));
});
app.post('/auth', (request, response) => {
var username = request.body.username;
var password = request.body.password;
if (username && password) {
connection.query('SELECT * FROM accounts WHERE username = ? AND password = ?', [username, password], (error, results, fields) => {
if (results.length > 0) {
request.session.loggedin = true;
request.session.username = username;
response.redirect('/home'); // This works
} else {
request.session.loggedin = false;
response.send('<script>alert("Incorrect Username and/or Password!")</script>');
response.redirect('/home'); // This doesnt work
}
response.end();
});
} else {
response.send('Please enter Username and Password!');
response.end();
}
});
app.get('/home', (request, response) => {
if (request.session.loggedin) {
response.send('Welcome back, ' + request.session.username + '!');
} else {
response.send('Please login to view this page!');
}
response.end();
});
app.listen(3000);
Ive put up my whole app.js but after authorizing the users login, im trying to redirect to the "/home" which works in the if case but not in the else. This is after the query in the code. The error Im getting is the following, and I really cant make heads or tails of it:
C:\xampp\htdocs\students\node_modules\mysql\lib\protocol\Parser.js:437
throw err; // Rethrow non-MySQL errors
^
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
←[90m at ServerResponse.setHeader (_http_outgoing.js:485:11)←[39m
at ServerResponse.header (C:\xampp\htdocs\students\node_modules\←[4mexpress←[24m\lib\response.js:771:10)
at ServerResponse.location (C:\xampp\htdocs\students\node_modules\←[4mexpress←[24m\lib\response.js:888:15)
at ServerResponse.redirect (C:\xampp\htdocs\students\node_modules\←[4mexpress←[24m\lib\response.js:926:18)
at Query.<anonymous> (C:\xampp\htdocs\students\app.js:39:26)
at Query.<anonymous> (C:\xampp\htdocs\students\node_modules\←[4mmysql←[24m\lib\Connection.js:526:10)
at Query._callback (C:\xampp\htdocs\students\node_modules\←[4mmysql←[24m\lib\Connection.js:488:16)
at Query.Sequence.end (C:\xampp\htdocs\students\node_modules\←[4mmysql←[24m\lib\protocol\sequences\Sequence.js:83:24)
at Query._handleFinalResultPacket (C:\xampp\htdocs\students\node_modules\←[4mmysql←[24m\lib\protocol\sequences\Query.js:149:8)
at Query.EofPacket (C:\xampp\htdocs\students\node_modules\←[4mmysql←[24m\lib\protocol\sequences\Query.js:133:8) {
code: ←[32m'ERR_HTTP_HEADERS_SENT'←[39m
}
[nodemon] app crashed - waiting for file changes before starting...
Here, you attempt to use res.redirect after using res.send to send an HTML string in a response. res.send comes from the Express framework and takes in data as a parameter. res.send then will then check the structure of the parameter and set the corresponding header as well as an ETag attribute in the header. It essentially implements res.write, res.setHeaders and then finally res.end, which comes from the Nodejs core and results in a "closing" of the response, effectively rendering many of the response object methods unusable since they require the response object to be "open". res.redirect is one such method that cannot be used after res.end is called (i.e. the response has been closed/is "over"). In other frameworks, a thread is dedicated to an http response and stops when the response itself has been closed. This isn't the case in Nodejs.
I would consider sending a response to the client via res.send that the client can look for as a signal to do a redirect, perhaps using something like window.location and trigger an alert.
I found these two threads to be very helpful:
Why can I execute code after "res.send"?
What is the difference between res.end() and res.send()?
You are sending the response first and then trying to redirect,that is causing the error as the connection will be closed once response is sent.
Try to use 'next' middleware in the post callback like
app.post("auth",(req,res,next)=>{
/* Your auth logic and in the else part use next for redirection */
next('redirectionRoute')
/* Or you can keep res.redirect() and remove the res.send() in else and use appropriate route handler for the redirect route - This is better way * /
})
I am trying to make Firebase authentication work on the server.
'use strict';
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const express = require('express');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser')();
const cors = require('cors')({origin: true});
//const expressSanitizer = require('express-sanitizer');
const app = express();
// Express middleware that validates Firebase ID Tokens passed in the Authorization HTTP header.
// The Firebase ID token needs to be passed as a Bearer token in the Authorization HTTP header like this:
// `Authorization: Bearer <Firebase ID Token>`.
// when decoded successfully, the ID Token content will be added as `req.user`.
const validateFirebaseIdToken = (req, res, next) => {
console.log('Check if request is authorized with Firebase ID token');
if ((!req.headers.authorization || !req.headers.authorization.startsWith('Bearer ')) &&
!(req.cookies && req.cookies.__session)) {
console.error('No Firebase ID token was passed as a Bearer token in the Authorization header.',
'Make sure you authorize your request by providing the following HTTP header:',
'Authorization: Bearer <Firebase ID Token>',
'or by passing a "__session" cookie.');
res.redirect("/login");
return;
}
let idToken;
if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
console.log('Found "Authorization" header');
// Read the ID Token from the Authorization header.
idToken = req.headers.authorization.split('Bearer ')[1];
} else if(req.cookies) {
console.log('Found "__session" cookie');
// Read the ID Token from cookie.
idToken = req.cookies.__session;
} else {
// No cookie
res.redirect("/login");
return;
}
admin.auth().verifyIdToken(idToken).then((decodedIdToken) => {
console.log('ID Token correctly decoded', decodedIdToken);
req.user = decodedIdToken;
return next();
}).catch((error) => {
console.error('Error while verifying Firebase ID token:', error);
res.redirect("/login");
});
};
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static("/public"));
app.use(cors);
app.use(cookieParser);
//app.use(expressSanitizer());
//app.use(validateFirebaseIdToken);=
app.set("view engine", "ejs");
// This HTTPS endpoint can only be accessed by your Firebase Users.
// Requests need to be authorized by providing an `Authorization` HTTP header
// with value `Bearer <Firebase ID Token>`.
exports.app = functions.https.onRequest(app);
app.post("/login", (request, response) => {
var idToken = request.body.token;
console.log("REQUEST BODY = " + idToken);
response.header("Authorization" , "Bearer " + idToken);
return response.redirect("dashboard");
});
app.get("/dashboard", validateFirebaseIdToken, (request, response) => {
response.redirect("/dashboard/new");
});
In the /login POST route, I am receiving the idToken as expected (and showed in the logs). It seems though, that the response is unable to preserve/maintain the header property Authentication: Bearer <Firebase ID token> set beforehand.
In fact, I sent a GET request in Postman to /dashboard by getting the idToken printed by the logs and setting it in the header of the request like Authorization: Bearer <idToken> and it worked perfectly.
Here it says that redirects are in fact new HTTPS requests and therefore don't preserve the header set in the response. What should I do in this case?
You have to send the Authorization header with every request. HTTPS functions are stateless. They don't remember anything from a prior request. So, you shouldn't depend on redirect behavior to retain state. Instead, the client needs to figure out where to go next and make the next request itself.
I'm making an REST API that uses JWTs for authentication, but to test my endpoints I want to mock the middleware that verifys the JWTs.
I've defined a middleware for checking the token:
// middlewares/auth.js
nJwt = require('njwt');
nconf = require('nconf');
module.exports = {
verifyToken: function(req, res, next) {
// check header or url parameters or post parameters for token
var token = req.body.token || req.query.token || req.headers['x-access-token'];
// decode token
if (token) {
// verifies secret and checks exp
nJwt.verify(token, nconf.get("auth:secret"), function(err, verifiedJwt) {
if (err) {
return res.json({ success: false, message: 'Failed to authenticate token.' });
} else {
// if everything is good, save to request for use in other routes
req.userId = verifiedJwt.body.sub;
next();
}
});
} else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
}
}
Then in my routers I can import and use this middleware on protected endpoints
// routes/someRoutes.js
var router = require('express').Router();
var verifyToken = require('../middlewares/auth').verifyToken;
router.use(verifyToken);
// define routes
Now I want to mock this middleware out so that I can test the endpoints without need a valid token. I've tried doing using chai/mocha/sinon but don't have much luck
// tests/someRoutes.js
var auth = require('../middlewares/auth');
var sinon = require('sinon');
describe('someRoute', function() {
var verifyTokenStub;
before(function (done) {
verifyTokenStub = sinon.stub(auth, 'verifyToken', function(req, res, next) {
req.userId='test-id';
next();
});
});
});
But this is still calling the original verifyToken method. Any help is much appreciated!
Change your middlewares/auth.js file check process.env.NODE_ENV.
Example:
// middlewares/auth.js
...
// decode token
if (token || process.env.NODE_ENV === 'test') {//Changes in this string
// verifies secret and checks exp
nJwt.verify(token, nconf.get("auth:secret"), function(err, verifiedJwt) {
...
If you use supertest or package with same functionality, the run mocha with environment variable - NODE_ENV=test mocha.
If testing your application with full run, then you should start it with environment variable - NODE_ENV=test npm start.
It's not mocking, but I hope it will help you.