From aa325872598f6f37b1c921224a934d0928716e56 Mon Sep 17 00:00:00 2001 From: rainbow napkin Date: Wed, 22 Oct 2025 20:17:53 -0400 Subject: [PATCH] Server now auto-magically nukes expired remember me tokens on startup and at midnight UTC. --- src/schemas/user/rememberMeSchema.js | 52 ++++++++++++++++++++++++---- src/utils/scheduler.js | 6 +++- 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/schemas/user/rememberMeSchema.js b/src/schemas/user/rememberMeSchema.js index dfb925b..32df58e 100644 --- a/src/schemas/user/rememberMeSchema.js +++ b/src/schemas/user/rememberMeSchema.js @@ -80,12 +80,6 @@ rememberMeToken.pre('save', async function (next){ next(); }); -//Methods -rememberMeToken.methods.checkToken = async function(token){ - //Compare ingested token to saved hash - return await hashUtil.compareRememberMeToken(token, this.token); -} - //statics rememberMeToken.statics.genToken = async function(userDB, pass){ //Normally I'd use userModel auth, but this saves on DB calls and keeps us from having to refrence the userModel directly @@ -154,4 +148,50 @@ rememberMeToken.statics.authenticate = async function(id, token, failLine = "Bad } } +/** + * Schedulable function for processing expired remember me tokens + */ +rememberMeToken.statics.processExpiredTokens = async function(){ + //Pull all tokens from the DB + //Tested finding request by date, but mongoose kept throwing casting errors. + //This seems to be an intermittent issue online. Maybe it will work in a future version? + const tokenDB = await this.find({}); + + //Fire em all off at once without waiting for the last one to complete since we don't fuckin' need to + for(let tokenIndex in tokenDB){ + //pull token from tokenDB by index + const token = tokenDB[tokenIndex]; + + //If the token hasn't been processed and it's been expired + if(token.getDaysUntilExpiration() <= 0){ + //Delete the token + await token.deleteOne(); + } + } +} + +//Methods +/** + * Intakes a plaintext token string and compares it to the hashed remember me token from the database + * @param {String} token - Plaintext token retrieved from browser cookie + * @returns {Boolean} Comparison result + */ +rememberMeToken.methods.checkToken = async function(token){ + //Compare ingested token to saved hash + return await hashUtil.compareRememberMeToken(token, this.token); +} + +/** + * Returns number of days until token expiration + * @returns {Number} Number of days until token expiration + */ +rememberMeToken.methods.getDaysUntilExpiration = function(){ + //Get request date + const expirationDate = new Date(this.date); + //Get expiration days and calculate expiration date + expirationDate.setDate(expirationDate.getDate() + daysToExpire); + //Calculate and return days until request expiration + return ((expirationDate - new Date()) / (1000 * 60 * 60 * 24)).toFixed(1); +} + module.exports = mongoose.model("rememberMe", rememberMeToken); \ No newline at end of file diff --git a/src/utils/scheduler.js b/src/utils/scheduler.js index f1a09bc..a0851ee 100644 --- a/src/utils/scheduler.js +++ b/src/utils/scheduler.js @@ -22,9 +22,9 @@ const {userModel} = require('../schemas/user/userSchema'); const userBanModel = require('../schemas/user/userBanSchema'); const passwordResetModel = require('../schemas/user/passwordResetSchema'); const emailChangeModel = require('../schemas/user/emailChangeSchema'); +const rememberMeModel = require('../schemas/user/rememberMeSchema'); const channelModel = require('../schemas/channel/channelSchema'); const sessionUtils = require('./sessionUtils'); -const { email } = require('../validators/accountValidator'); /** * Schedules all timed jobs accross the server @@ -42,6 +42,8 @@ module.exports.schedule = function(){ cron.schedule('0 0 * * *', ()=>{passwordResetModel.processExpiredRequests()},{scheduled: true, timezone: "UTC"}); //Process expired email change requests every night at midnight cron.schedule('0 0 * * *', ()=>{emailChangeModel.processExpiredRequests()},{scheduled: true, timezone: "UTC"}); + //Process expired remember me tokens every night at midnight + cron.schedule('0 0 * * *', ()=>{rememberMeModel.processExpiredTokens()},{scheduled: true, timezone: "UTC"}); } /** @@ -58,6 +60,8 @@ module.exports.kickoff = function(){ passwordResetModel.processExpiredRequests(); //Process expired email change requests that may have expired since last restart emailChangeModel.processExpiredRequests(); + //Process expired remember me tokens that may have expired since last restart + rememberMeModel.processExpiredTokens() //Schedule jobs module.exports.schedule();