Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"expiresIn" option not working with sequelize object #147

Closed
gugol2 opened this issue Dec 9, 2015 · 21 comments
Closed

"expiresIn" option not working with sequelize object #147

gugol2 opened this issue Dec 9, 2015 · 21 comments

Comments

@gugol2
Copy link

gugol2 commented Dec 9, 2015

The token is created but never expires. I am getting the object from a query to a relational db with the sequelize package. Code example:

models.User.findOne({ where: {username: req.body.name} }) //Returns an object user or null
.then(function(user) {
    if (user) {
        console.log(typeof user); //Logs "object"
        // Check if password matches
        if (user.password != req.body.pass) {
            res.json({ message: 'Authentication failed. Wrong password.' });
        } else {
            // If user is found and password is right create a token
            var token = jwt.sign(
                user, 
                'shhhhh', 
                {
                    expiresIn: 60 // expires in 1 minute
                }
            );

            // Shows the token
            console.log(token);
        }   
    } else {
        console.log("User not found");
    }

}).catch(function(error){
    whatever...
}); 
@MariusRumpf
Copy link

I am using a similar method that works for me, except that I only use only the username as token:

    User.findOne({username: username}, (err, user) => {
      if (err) {
        return done(err, false);
      }
      if (!user) {
        return done(null, false, {message: 'Invalid credentials'});
      }
      bcrypt.compare(password, user.password, (err, isMatching) => {
        if (err) {
          return done(err, false);
        }
        if (!isMatching) {
          return done(null, false, {message: 'Invalid credentials'});
        }

        var token = jwt.sign({user: user.username}, config.secrets.jwt, {
          expiresIn: '10m',
          algorithm: 'HS256'
        });
        user.token = token;
        User.update({_id: user._id}, {token: user.token, reauth: false}, () => {
          user = _.pick(user, ['username', 'token']);
          return done(null, user);
        });
      });
    });

What infos does decoding your token give on http://jwt.io/ ?

@eXtreaL
Copy link

eXtreaL commented Dec 17, 2015

I'm using a similar way to sign the token and am occurring the same problem at the moment. Everything works fine, however the token doesn't seem to expire. I'm storing the token in the localstorage of the browser.

@MariusRumpf
Copy link

Didn't update for some time, will try it in the next weeks again

@gugol2
Copy link
Author

gugol2 commented Dec 21, 2015

@MariusRumpf I got "Invalid signature" although I am using HS256.
In the details I get "Decoded edit the payload and secret (only HS256 supported)"

I must be doing something wrong because even with dummy data it does not pass the validation of jwt.io:

models.User.findOne({ where: {username: req.body.name} }) //Returns an object user or null
    .then(function(user) {
        if (user) {
        console.log(typeof user); //Logs "object"
        // Check if password matches
        if (user.password != req.body.pass) {
            res.json({ message: 'Authentication failed. Wrong password.' });
        } else {
            // If user is found and password is right create a token
            var profile = {
                first_name: 'John',
                last_name: 'Doe',
                email: 'john@doe.com',
                id: 123
            };
            //I make sure it is a json object
            var token = jwt.sign(
                profile, 
                'shhhhh', 
                {
                    algorithm: 'HS256',
                    expiresIn: 60 // expires in 1 minute
                }
                );

            // Shows the token
            console.log(token);
        }   
    } else {
        console.log("User not found");
    }

    }).catch(function(error){
        whatever...
    }); 

@pjanfred
Copy link

@gugol2 I think, i have the same problem.

But i have a workaround:
jwt.sign({user: user}, app.get('superSecret')...

It's not the best way, but is it a way.

@gugol2
Copy link
Author

gugol2 commented Dec 23, 2015

@pjanfred that did not solve anything in my case.

Here is the result I get from jwt.io

@pjanfred
Copy link

@gugol2 Can you tell me what debugger you use? I will test it with my token.

@jfromaniello
Copy link
Member

@pjanfred thats https://jwt.io

@gugol2 In your screenshot I can see that the token doesn't have expiration. what version of jsonwebtoken are you using? You might be using an older version that doesn't support the expiresIn parameter, it used to be called expiresInSeconds

@pjanfred
Copy link

@jfromaniello Thank you :)

@gugol2 At the moment i don´t have further ideas. Can you show us the part of your sourcecode?

@MariusRumpf
Copy link

@gugol2 Tested my posted example again, it works correct for me. The tokens decode as expected on https://jwt.io

@quintenvk
Copy link

I, too, ran into this issue upon getting back an object from MongoDB's model I created. I passed the object itself directly on to jwt.sign() as a payload.

Debugging within the jwt.sign() function pointed out that it did call payload.exp = ... setting. However, this did not affect the object.

The solution for me was to create a new object and pass it on as payload. This might be due to ES2015's const?

In either case, I'd propose that the payload could be a new object with parameters and the original payload that's passed on to the function is encapsulated in this new object, like so:

var payload = {
     exp: expirationTimestamp,
     data: originalPayload
};

This would prevent any properties from the original payload from being overwritten.

@lagoasoft-lucasschmidt
Copy link

I had this problem as well, it is because Mongoose and other libraries extend the stringify/toJSON methods of those model objects. (I dont remember how)

In case of mongoose, its enough for you to call modelObject.toJSON() and it solves the issue. Just tested here.

I think this library should add some sort of mechanism to verify this, otherwise, lots of developers may be implementing JWT with expirationIn properly, but without knowing it doesnt work. This is basically critical security issue. Obviously this is developer`s fault in the end, but ... maybe its good to help everyone out.

@gugol2
Copy link
Author

gugol2 commented Jan 3, 2016

@jfromaniello from my packaje.json:

"jsonwebtoken": "~5.4.1"

@ekoome
Copy link

ekoome commented Jan 3, 2016

Using version "jsonwebtoken": "^5.5.0" and getting error:

Unhandled rejection Error: "expiresIn" should be a number of seconds or string representing a timespan eg: "1d", "20h", 60

regardless of option use.

var token = jwt.sign(user.email, 'superSecret', { expiresIn: '1d' }); // Expire in 24 nours

@jfromaniello
Copy link
Member

I think I found the cause of the problems reported here. This library was trying to modify the input payload directly which was a bad design, this might broke when the payload has been sealed with Object.seal or Object.freeze.

Please try with version: 5.5.2 and let me know.

@ggviana
Copy link

ggviana commented Jan 5, 2016

I had this problem too. Switching back to 5.4.1 corrected it.

@gugol2
Copy link
Author

gugol2 commented Jan 6, 2016

After all the help provided here I kind of found the issue. I apologize for my delayed answers and my lack of thoroughness along the procedure.

  • @MariusRumpf first example worked ok but I didn't know why nor how to use http://jwt.io properly at that moment.
  • The workaround of @pjanfred was also correct, just like @MariusRumpf passing an object straight away to jwt.
  • @lucastschmidt hit the nail though, "Mongoose and other libraries extend the stringify/toJSON methods of those model objects".

So my conclusions are (using version: "jsonwebtoken": "~5.4.1") to get your token to expire you can do any of these two things:

  • Create and fill the object yourself and pass it to jwt (Sequelize or Mongoose objects may not be in JSON notation).
  • Call modelObject.toJSON() on your object before passing it to jwt.

IMHO probably the first option is better because you can pass just the minimum info in your token.

Thanks to everybody.
Cheers!

@jfromaniello
Copy link
Member

@gugol2 for mongoose documents you should use jwt.sign(doc.toObject(), {..});

notice the toObject() call.

@lagoasoft-lucasschmidt
Copy link

Correct @gugol2 , you shouldnt pass the plain mongoose object, since that object may contain properties like hashed password or some other private stuff that shouldnt be available on a token (that can be freely read by anyone).

@leokrlewis
Copy link

I solved this by doing the following. If your data is a string such as encrypting your payload before jwt tokenizing.
var options = {
'expiresIn': '1h'
};
var payload = { encTokenData: encStringTokenData };
var token = jwt.sign(payload, 'yoursecret', options);

@IMalaniak
Copy link

use dataValues, not just Obj.

ex:
const token = jwt.sign(user.dataValues, env.SECRET, {
expiresIn: 604800 // 1 week
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests