0

I am trying to update forgetpassword_token to null in mongodb document after 24 hours of generating forgetpassword_token. So I am using Mongoose schema middleware and setTimeout, but setTimeout is not working.

I have tried to implement async await which is also not working as per my result.

CompanySchema.post('updateOne',true, function(doc,next){
    next();
       setTimeout(this.update({},{ $set: { forgetpassword_token: null } }).then(result=>{
           console.log(result);
                   }),10000000000);
       });
lifeisfoo
  • 15,478
  • 6
  • 74
  • 115
Deeksha gupta
  • 659
  • 6
  • 15

1 Answers1

1

The main problem here is that this implementation is flawed, because if your node application is restarted during the 24-hour window, your timeout will disappear (is an in memory object, not persisted) and the token will remain active, exposing you to security risks.

Manual token verification

A very common solution is to save the token_expiration_date alongside the token, making a date comparison during the related password reset request. If the token_expiration_date is expired, the request return an error and the server must delete the token on the db.

You can also make the opposite: store the token_creation_date and the max-token-ttl in your app code (e.g. 24 hours). In any case you make the date comparison at request time.

@NikKyriakides suggested (see comments) a more sophisticated version of this approach: you create a single JWT token that contains itself the expiration date. When the user request the reset password page you need only to verify if the token is valid calling a single method (no manual date comparison).

The Mongo expire option

A more elegant and effective solution is to create a different mongoose schema for your forgetpassword_token and use the native mongo/mongoose expire option to auto delete documents after a fixed time from their creation.

const secondsInADay = 60 * 60 * 24;

const tokenSchema = mongoose.Schema({
    value: String
}, {timestamps: true});

tokenSchema.index({createdAt: 1},{expireAfterSeconds: secondsInADay});
const Token = mongoose.model('Token', tokenSchema);

Then add to your existing CompanySchema a reference to this schema:

forgetpassword_token: {type: mongoose.Schema.Types.ObjectId, ref: 'Token'}

A lot of question exists on this topic, so please also check them alongside with the related mongoose documentation.

The job scheduler

Another approach is to use a job scheduler like agenda to hourly check for expired tokens and delete them. Yes, you can write a setTimeout based check as a module for your app, but if the right tools exists yet, why don't use it? Also check @NikKyriakides comments below for potential drawbacks of this solution.

lifeisfoo
  • 15,478
  • 6
  • 74
  • 115
  • Can't you like encode/encrypt the creation datetime in the token itself, then decrypt it and check if it's expired based on current time? IRRC [JWT](https://jwt.io/) does something similar. – nicholaswmin Mar 28 '19 at 10:24
  • Yes @NikKyriakides, this is another possible approach. But when security is involved I'll prefer to write less code/logic as possible, because when writing the encoding/comparison procedure we can add risky bugs to it. And during the lifecycle of the project the code can be inadvertently changed. Moreover, delegating a specific component (mongo itself or agenda) to _actively_ looking for expired tokens, we can be sure that they are deleted as soon as possible. Security is also about tiny details. Obviously this is my POV. – lifeisfoo Mar 28 '19 at 10:40
  • I think you're violating [KISS](https://en.wikipedia.org/wiki/KISS_principle) with what you're suggesting. What if your job scheduler goes down? What if the DB has a lock when the job scheduler runs? Doesn't that mean that expired tokens won't be deleted? What if I attempt to execute forgot password after token is expired but before job scheduler runs? Code can be inadvertently be changed on what you're proposing as well. Someone modified your job scheduler to simply.. well not run. Passive measures are always better than active measures. – nicholaswmin Mar 28 '19 at 12:05
  • Also `when security is involved I'll prefer to write less code/logic as possible...`. How is involving and configuring a job scheduler that needs to be always on, less code than simply writing a middleware that decodes a JWT and compares the datetime with currenttime? I think I don't need to say that I'm not attacking you, but rather the logic of your proposed solution. – nicholaswmin Mar 28 '19 at 12:07
  • @NikKyriakides my statement was mainly referred to the mongo expire option. Yes, the scheduler solution can be overkilling and has its cons, but in some cases it could be required (e.g. notify the user about that or when more logic is needed in the token management). I used many times in the past a solution based on date comparison, and now I'll update the answer with another solution based on your suggestion. Thank you also for your criticism: when it's based on fatcs it's always useful. – lifeisfoo Mar 28 '19 at 12:57