markovchain
markovchain

Reputation: 503

Using ObjectID with jwt.sign() and verify()

Upon logging in to my system, I run the MongoDB _id of the logged in user through the jsonwebtoken's sign method. It returns to me a hash, which I then put into the session headers of every subsequent request that the client makes to my server.

I now want to decode the session and recover the string _id from the headers, so I run it against the jsonwebtoken's verify method. I am not doing this for authentication (that's already handled by looking the session up in a DB). I'm recovering the _id so I can log the user's activities in a separate collection. I run the verify function through a middleware and save the decoded result in req.decoded.

However, when I log req.decoded, it is a BSON object, rather than a string:

{ _bsontype: 'ObjectID',
  id:
   { type: 'Buffer',
     data: [ 89, 128, 145, 134, 118, 9, 216, 20, 175, 174, 247, 33 ] },
  iat: 1501903389,
  exp: 1501989789 }

How can I recover the _id value from this object, such that I can look for this record again in my collections?

What I've tried

I've tried the following, without success:

Upvotes: 2

Views: 2534

Answers (1)

Neil Lunn
Neil Lunn

Reputation: 151132

This of course is subjective to how you actually supply the data to .sign() in the first place, being the difference of simply supplying the "object" of ObjectID or by supplying the value instead.

This is actually covered in the general usage for .sign() as :

If payload is not a buffer or a string, it will be coerced into a string using JSON.stringify.

So in a nutshell that version is going to "stringify" the Object form an require some "digging". In your form the decoded object then has properies of id with a sub-property of data which contains an array of bytes than can be converted into a Buffer. So that's what you do:

  let newId = Buffer.from(req.decoded.id.data).toString('hex');

And newId would then be a "string" represented by the 'hex' encoded values of the bytes. This of course would be translated by mongoose into an ObjectId when issued in any "query" or "update" as matching the schema for _id.

Of course the "alternative" would be simply to .sign() using the .toString() value from the ObjectId in the first place. Then the result of .verify() would simply the be "hex string" that as supplied, rather than the JSON.stringify result on the ObjectID itself.

To demonstrate with a listing:

const bson = require('bson'),
      jwt = require('jsonwebtoken');

// Stored ObjectID
console.log("Round 1");
(function() {
  let id = new bson.ObjectID();
  console.log("Created: %s", id);

  let token = jwt.sign(id,'shhh');                // Supply value as ObjectID
  let decoded = jwt.verify(token,'shhh');

  console.log("Interim");
  console.log(decoded);

  let newId = Buffer.from(decoded.id.data).toString('hex');
  console.log("Decoded: %s", newId);
})();

console.log("\nRound 2");
// Stored String value
(function() {

  let id = new bson.ObjectID();
  console.log("Created: %s", id);

  let token = jwt.sign(id.toString(), 'shhh');    // Supply value as string
  let decoded = jwt.verify(token,'shhh');

  console.log("Decoded: %s", decoded);

})();

Gives the output, showing the input values and decoded values:

Round 1
Created: 59857328090c497ce787d087
Interim
{ _bsontype: 'ObjectID',
  id:
   { type: 'Buffer',
     data: [ 89, 133, 115, 40, 9, 12, 73, 124, 231, 135, 208, 135 ] },
  iat: 1501917992 }
Decoded: 59857328090c497ce787d087

Round 2
Created: 59857328090c497ce787d088
Decoded: 59857328090c497ce787d088

And demonstrates both forms of usage for supplying the value to .sign() and what comes out from the subsequent .verify() calls.

Upvotes: 2

Related Questions