Search code examples
javascriptjsonexpresssequelize.jsjson-web-token

Circular reference blocks jsonwebtoken.sign cause of JSON.stringify


I'm using Express and Sequelize to make a basic user authentication based upon this tutorial

When I want to sign a token to a user I get an error telling me I'm trying to JSON.stringify() a circular reference which cannot be done. Therefor an error is thrown and I can't assign the token to the user.

OR I am doing something wrong when finding my user in the database which makes a circular reference OR I just need to find a solution to break the circular reference I suppose. Anyone who could explain me which of the two it is?

The full error is:

TypeError: Converting circular structure to JSON
    at Object.stringify (native)
    at toString (/Users/Desktop/express-jwt/node_modules/jws/lib/tostring.js:9:15)
    at jwsSecuredInput (/Users/Desktop/express-jwt/node_modules/jws/lib/sign-stream.js:12:34)
    at Object.jwsSign [as sign] (/Users/Desktop/express-jwt/node_modules/jws/lib/sign-stream.js:22:22)
    at Object.module.exports [as sign] (/Users/Desktop/express-jwt/node_modules/jsonwebtoken/sign.js:144:16)
    at Model.User.findOne.then.user (/Users/Desktop/express-jwt/server/index.js:69:27)
    at Model.tryCatcher (/Users/Desktop/express-jwt/node_modules/bluebird/js/release/util.js:16:23)
    at Promise._settlePromiseFromHandler (/Users/Desktop/express-jwt/node_modules/bluebird/js/release/promise.js:510:31)
    at Promise._settlePromise (/Users/Desktop/express-jwt/node_modules/bluebird/js/release/promise.js:567:18)
    at Promise._settlePromise0 (/Users/Desktop/express-jwt/node_modules/bluebird/js/release/promise.js:612:10)
    at Promise._settlePromises (/Users/Desktop/express-jwt/node_modules/bluebird/js/release/promise.js:691:18)
    at Async._drainQueue (/Users/Desktop/express-jwt/node_modules/bluebird/js/release/async.js:138:16)
    at Async._drainQueues (/Users/Desktop/express-jwt/node_modules/bluebird/js/release/async.js:148:10)
    at Immediate.Async.drainQueues (/Users/Desktop/express-jwt/node_modules/bluebird/js/release/async.js:17:14)
    at runCallback (timers.js:574:20)
    at tryOnImmediate (timers.js:554:5)

My index for the server is:

const express = require(`express`);
const app = express();
const bodyParser = require(`body-parser`);
const morgan = require('morgan');

const jwt = require('jsonwebtoken'); // used to create, sign, and verify tokens
const config = require('./config'); // get our config file
const db = require(`./models`);
const User = global.db.User;

const port = process.env.PORT || 8080;

db.sequelize.sync().then(() => {
    console.log(`Express server listening on port ${port}`);
});

app.set('superSecret', config.secret);

app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());

app.use(morgan('dev'));

app.get('/', (req, res) => {
  res.send('Hello! The API is at http://localhost:' + port + '/api');
});

app.listen(port);
console.log('Magic happens at http://localhost:' + port);

app.get('/setup', (req, res) => {

  db.sequelize.sync().then(() => {
    return User.create({
      username: 'Kevin frafster',
      password: 'password',
      admin: true
    })
    .then(addedUser => {
      console.log(addedUser.get({
        plain: true
      }));
    })
    .catch(err => {
      res.json(err);
    });
  });

});

// API ROUTES -------------------

// get an instance of the router for api routes
const apiRoutes = express.Router();

apiRoutes.post('/authenticate', (req,res) => {
  User.findOne({
    where: {username: req.body.username}
  }).then(user => {
    if (!user) {
      res.json({ success: false, message: 'Authentication failed. User not found.'});
    }else{
      // console.log(user);
      if (user.password != req.body.password) {
        res.json({ success: false, message: 'Authentication failed. Wrong password.' })
      }else{

        const token = jwt.sign(user, app.get('superSecret'), {
          expiresIn: 60*60*24
        });

        res.json({
          succes: true,
          message: 'Enjoy your token!',
          token
        });
      }
    }
  }).catch(err => {
    res.status(500).json(err);
  })
});

// TODO: route to authenticate a user (POST http://localhost:8080/api/authenticate)

// TODO: route middleware to verify a token

// route to show a random message (GET http://localhost:8080/api/)
apiRoutes.get('/', (req, res) => {
  res.json({ message: 'Welcome to the coolest API on earth!' });
});

// route to return all users (GET http://localhost:8080/api/users)
apiRoutes.get('/users', (req, res) => {
  User.findAll({})
    .then(users => {
      res.json(users);
    })
    .catch(err => {
      res.json(err);
    });
});

// apply the routes to our application with the prefix /api
app.use('/api', apiRoutes);

Solution

  • Well, the answer was totally peanuts.

    1) Make new object and assign payload to it

    const payload = {username: user.username, password: user.password};

    2) Use the new object to assign token to

    const token = jwt.sign(payload, app.get('superSecret'), {
      expiresIn: 60*60*24
    });