1

First off, I read all other StackOverflow answers and GitHub Issues and none of them seem to have solved my problem. With the following code (some of it is deprecated, sorry for the dirty code):

var express = require('express');
var fs = require('fs');
var path = require('path');
var parser = require('body-parser');
//Initializing the Express Framework
const app = express();
const mongoose = require('mongoose');
const uuidv4 = require ('uuid/v4');
var jose = require('node-jose');
var secureRandom = require('secure-random');
var bcrypt = require('bcrypt');
const jwt = require('express-jwt');
const jwt2 = require('jsonwebtoken');
var userSchema = new mongoose.Schema({
  username: {type:String,unique:true},
  password: String
},{timestamps:true})

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";

var signingKey = secureRandom(256, {type: 'Buffer'});

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Add headers
app.use(function (req, res, next) {
  res.removeHeader('X-Powered-By');
  res.setHeader('Access-Control-Allow-Origin', 'http://localhost:4200');
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
  res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type,Authorization');
  res.setHeader('Access-Control-Allow-Credentials', true);
  next();
});

var claims = {
  iss: "http://example.com/",  // The URL of your service
  sub: "users/admin1",    // The UID of the user in your system
  name: "User Name",
  scope: "self, admins"
}

userSchema.pre('save', function (next) {
  var user = this;
  if (!user.isModified('password')) {return next()};
  bcrypt.hash(user.password,10).then((hashedPassword) => {
      user.password = hashedPassword;
      next();
  })
}, function (err) {
  next(err);
})
userSchema.methods.comparePassword=function(candidatePassword,next){    bcrypt.compare(candidatePassword,this.password,function(err,isMatch){
      if(err) return next(err);
      next(null,isMatch)
  })
}
module.exports = mongoose.model("user", userSchema);

function protectRoute(req,res,next){
  // if user exists the token was sent with the request
  if(req.user){
   //if user exists then go to next middleware
     next();
  }
// token was not sent with request send error to user
  else{
     res.status(500).json({error:'login is required'});
  }
}

/* app.get('/protected',
  jwt({secret: 'shhhhhhared-secret'}),
  function(req, res) {
    if (!req.user.admin) return res.sendStatus(401);
    res.sendStatus(200);
  }); */

MongoClient.connect(url,  {
  useUnifiedTopology: true,
  useNewUrlParser: true,
  },function(err, db) {
  if (err) throw err;
  var dbo = db.db("demo");
  dbo.collection("demo").find({}).toArray(function(err, result) {
    if (err) throw err;
    console.log(result);
    db.close();
  });
}); 

readJsonFile = function(fname, res) {

    fs.readFile(fname, 'utf8', function (err,data) {
        if (err) {
            console.log(err);
        }
        res.send(data.toString().replace(/\n|\r/g,''));
    });

}


app.get('/', function (req, res) {
  res.send('Hello World!');
});

app.get('/users', function (req, res) {
    MongoClient.connect(url,  {
    useUnifiedTopology: true,
    useNewUrlParser: true,
    },function(err, db) {
    if (err) throw err;
    var dbo = db.db("demo");
    dbo.collection("users").find({}).toArray(function(err, result) {
      if (err) throw err;
      res.send(result);
      db.close();
    });
  }); 
  console.log("GET UserCollection from MongoDB");
});

app.post('/users/:id', verifyToken, function (req, res){
  jwt2.verify(req.token, 'secretkey', (err, auth) =>{
    if(err){
      res.sendStatus(403);

    } else {
      res.json({
        message: 'User created (in theory)',
        authData
      })
    }
  })
  res.json({
    message: 'User created (in theory)2'
  })
});

app.post('/api/login', function (req, res){
  // Mock user TODO: implement proper BackEnd!
  const user = {
    id:1,
    username:'brad',
    email: 'brad@gmail.com'
  }
  jwt2.sign({user}, 'secretkey', { expiresIn: '1h'}, (err, token) =>{
    res.json({
      token
    });
  });
});

app.get('/user', function (req, res) {
    readJsonFile('user.json', res);
  console.log("GET User");
});

// FORMAT OF TOKEN
// Authorization: Bearer <access_token>

// Verify Token
function verifyToken(req, res, next){
  // Get auth header value
  const bearerHeader = req.headers['authorization'];
  // Check if bearer is undefined
  if(typeof bearerHeader !== 'undefined'){
    // Split at the space
    const bearer = bearerHeader.split(' ');
    // Get token from array
    const bearerToken = bearer[1];
    // Set the token
    req.token = bearerToken;
    // Next middleware
    next();
    res.json({message: "Login successful"});
  }
    else{
      // Forbidden
      res.sendStatus(403);
      next();
    }
  }

app.listen(3000, function () {
  console.log('Listening on port 3000!');
});

if(process.env.NODE_ENV !== 'production') {
  process.once('uncaughtException', function(err) {
    console.error('FATAL: Uncaught exception.');
    console.error(err.stack||err);
    setTimeout(function(){
      process.exit(1);
    }, 100);
  });
}

After I run the app and POST the URL /users/5 I get the following errors and the app crashes:

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at ServerResponse.setHeader (_http_outgoing.js:485:11)
    at Array.write (C:\Users\User\Documents\Project\Project2\node_modules\finalhandler\index.js:285:9)
    at listener (C:\Users\User\Documents\Project\Project2\node_modules\on-finished\index.js:169:15)
    at onFinish (C:\Users\User\Documents\Project\Project2\node_modules\on-finished\index.js:100:5)
    at callback (C:\Users\User\Documents\Project\Project2\node_modules\ee-first\index.js:55:10)
    at IncomingMessage.onevent (C:\Users\User\Documents\Project\Project2\node_modules\ee-first\index.js:93:5)
    at IncomingMessage.emit (events.js:215:7)
    at endReadableNT (_stream_readable.js:1183:12)
    at processTicksAndRejections (internal/process/task_queues.js:80:21)
ReferenceError: authData is not defined
    at C:\Users\User\Documents\Project\Project2\src\app.js:134:9
    at C:\Users\User\Documents\Project\Project2\node_modules\jsonwebtoken\verify.js:223:12
    at getSecret (C:\Users\User\Documents\Project\Project2\node_modules\jsonwebtoken\verify.js:90:14)
    at Object.module.exports [as verify] (C:\Users\User\Documents\Project\Project2\node_modules\jsonwebtoken\verify.js:94:10)       
    at C:\Users\User\Documents\Project\Project2\src\app.js:127:8
    at Layer.handle [as handle_request] (C:\Users\User\Documents\Project\Project2\node_modules\express\lib\router\layer.js:95:5)    
    at next (C:\Users\User\Documents\Project\Project2\node_modules\express\lib\router\route.js:137:13)
    at verifyToken (C:\Users\User\Documents\Project\Project2\src\app.js:178:5)
    at Layer.handle [as handle_request] (C:\Users\User\Documents\Project\Project2\node_modules\express\lib\router\layer.js:95:5)    
    at next (C:\Users\User\Documents\Project\Project2\node_modules\express\lib\router\route.js:137:13)
    at Route.dispatch (C:\Users\User\Documents\Project\Project2\node_modules\express\lib\router\route.js:112:3)
    at Layer.handle [as handle_request] (C:\Users\User\Documents\Project\Project2\node_modules\express\lib\router\layer.js:95:5)    
    at C:\Users\User\Documents\Project\Project2\node_modules\express\lib\router\index.js:281:22
    at param (C:\Users\User\Documents\Project\Project2\node_modules\express\lib\router\index.js:354:14)
    at param (C:\Users\User\Documents\Project\Project2\node_modules\express\lib\router\index.js:365:14)
    at Function.process_params (C:\Users\User\Documents\Project\Project2\node_modules\express\lib\router\index.js:410:3)

I tried adding return and next statements without any avail.

P.S This helps to understand the problem:

This error occurs when you sent a response before and then you try to send response again. For this you have to check if there is any piece of code that is sending your response twice. Sometimes it happens due to asynchronous behavior of nodejs. Sometimes a process will be in event loop and we send response and when it finishes execution response will be sent again. So You can use callbacks or async await to wait for execution.

PP.S After following the code examples given in the answers now I get this error (it's more similar to the linked question now):

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at ServerResponse.setHeader (_http_outgoing.js:485:11)
    at ServerResponse.header (C:\Users\User\Documents\Project\Project2\node_modules\express\lib\response.js:771:10)
    at ServerResponse.send (C:\Users\User\Documents\Project\Project2\node_modules\express\lib\response.js:170:12)
    at ServerResponse.json (C:\Users\User\Documents\Project\Project2\node_modules\express\lib\response.js:267:15)
    at verifyToken (C:\Users\User\Documents\Project\Project2\src\app.js:176:9)
    at Layer.handle [as handle_request] (C:\Users\User\Documents\Project\Project2\node_modules\express\lib\router\layer.js:95:5)
    at next (C:\Users\User\Documents\Project\Project2\node_modules\express\lib\router\route.js:137:13)
    at Route.dispatch (C:\Users\User\Documents\Project\Project2\node_modules\express\lib\router\route.js:112:3)
    at Layer.handle [as handle_request] (C:\Users\User\Documents\Project\Project2\node_modules\express\lib\router\layer.js:95:5)
    at C:\Users\User\Documents\Project\Project2\node_modules\express\lib\router\index.js:281:22 
    at param (C:\Users\User\Documents\Project\Project2\node_modules\express\lib\router\index.js:354:14)
    at param (C:\Users\User\Documents\Project\Project2\node_modules\express\lib\router\index.js:365:14)
    at Function.process_params (C:\Users\User\Documents\Project\Project2\node_modules\express\lib\router\index.js:410:3)
    at next (C:\Users\User\Documents\Project\Project2\node_modules\express\lib\router\index.js:275:10)
    at C:\Users\User\Documents\Project\Project2\src\app.js:35:3
    at Layer.handle [as handle_request] (C:\Users\User\Documents\Project\Project2\node_modules\express\lib\router\layer.js:95:5)
Munchkin
  • 857
  • 5
  • 24
  • 51
  • @SuleymanSah I get a different trace and my code is different. There is no SetHeader error in my trace. – Munchkin Nov 12 '19 at 10:12
  • Update: now there actually is a setHeader error in my trace, but the solution is still clearly different from the linked question. – Munchkin Nov 12 '19 at 10:51

2 Answers2

3

When your code try to send responce multiple times at that time this error occur, Here is your working post API code

 app.post('/users/:id', verifyToken, function (req, res){
  jwt2.verify(req.token, 'secretkey', (err, auth) =>{
    if(err){
      res.sendStatus(403);

    } else {
      res.json({
        message: 'User created (in theory)',
        auth
      })
    }
  })
});

Just remove extra response code from that API

You have to remove next() keywork in verify token function

function verifyToken(req, res, next){
  // Get auth header value
  const bearerHeader = req.headers['authorization'];
  // Check if bearer is undefined
  if(typeof bearerHeader !== 'undefined'){
    // Split at the space
    const bearer = bearerHeader.split(' ');
    // Get token from array
    const bearerToken = bearer[1];
    // Set the token
    req.token = bearerToken;
    // Next middleware
    next();
  }
    else{
      // Forbidden
      res.sendStatus(403);
    }
  }

update verify function with this code

Ankur Patel
  • 478
  • 3
  • 6
0

In your app.post('/users/:id'..) you are sending response twice. So, you are getting the error second time because the the response is already send.

There is another error: authData is not defined because it will be auth.

    app.post('/users/:id', verifyToken, function (req, res){
      jwt2.verify(req.token, 'secretkey', (err, auth) => {
        if(err){
          res.sendStatus(403);
        } else {
          res.json({
            message: 'User created (in theory)',
            auth
          })
        }
      })
    });
Shihab
  • 2,641
  • 3
  • 21
  • 29