fix(auth): add single-use token login recovery flow

This commit is contained in:
Adolfo Reyna
2026-02-20 20:20:40 -05:00
parent c6d9dfd3c1
commit 469962d03c
4 changed files with 150 additions and 86 deletions

View File

@@ -41,10 +41,9 @@ const getDB = new Promise((resolve, reject) => {
DB.usersCol = db.db(DBName).collection("users");
DB.tokensCol = db.db(DBName).collection("tokens");
DB.invitationCol = db.db(DBName).collection("invitation");
// SECURITY PLAN (point #1):
// Add password reset token collection + TTL index, e.g.:
// DB.passwordResetTokensCol = db.db(DBName).collection("password_reset_tokens");
// DB.passwordResetTokensCol.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 });
DB.passwordLoginTokensCol = db.db(DBName).collection("password_login_tokens");
DB.passwordLoginTokensCol.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 }).catch(console.error);
DB.passwordLoginTokensCol.createIndex({ tokenHash: 1 }, { unique: true }).catch(console.error);
DB.checkSessionOnDB = async (session_id, user_sid)=>{
const temp_id = new mongo.ObjectID(session_id);
@@ -82,6 +81,42 @@ const getDB = new Promise((resolve, reject) => {
return DB.usersCol.findOne({ _id });
}
DB.createPasswordLoginToken = async (userId, tokenHash, expiresAt) => {
const userObjectId = mongo.ObjectID.isValid(userId) ? new mongo.ObjectID(userId) : userId;
const tokenDoc = {
userId: userObjectId,
tokenHash,
createdAt: new Date(),
expiresAt,
usedAt: null,
};
return DB.passwordLoginTokensCol.insertOne(tokenDoc).catch((err) => {
console.log(err);
return false;
});
};
DB.consumePasswordLoginToken = async (tokenHash) => {
const now = new Date();
const result = await DB.passwordLoginTokensCol.findOneAndUpdate(
{
tokenHash,
usedAt: null,
expiresAt: { $gt: now }
},
{
$set: { usedAt: now }
},
{
returnOriginal: false
}
).catch((err) => {
console.log(err);
return false;
});
return result?.value || null;
};
let usernamesCache = {}
DB.getUsernameByIdCache = async (userid)=>{
if(!userid) return {};