LibreChat/packages/data-schemas/src/methods/token.ts
Danny Avila fff1f1cf27
🔒 fix: Update Token Deletion To Prevent Undefined Field Queries (#9477)
* Refactor deleteTokens to use an array of conditions for querying, ensuring only specified fields are considered for deletion.
* Add error handling to prevent accidental deletion when no query parameters are provided.
* Update AuthService to match the new deleteTokens signature by passing an object instead of a string for email.
2025-09-05 17:26:02 -04:00

122 lines
3.3 KiB
TypeScript

import { IToken, TokenCreateData, TokenQuery, TokenUpdateData, TokenDeleteResult } from '~/types';
import logger from '~/config/winston';
// Factory function that takes mongoose instance and returns the methods
export function createTokenMethods(mongoose: typeof import('mongoose')) {
/**
* Creates a new Token instance.
*/
async function createToken(tokenData: TokenCreateData): Promise<IToken> {
try {
const Token = mongoose.models.Token;
const currentTime = new Date();
const expiresAt = new Date(currentTime.getTime() + tokenData.expiresIn * 1000);
const newTokenData = {
...tokenData,
createdAt: currentTime,
expiresAt,
};
return await Token.create(newTokenData);
} catch (error) {
logger.debug('An error occurred while creating token:', error);
throw error;
}
}
/**
* Updates a Token document that matches the provided query.
*/
async function updateToken(
query: TokenQuery,
updateData: TokenUpdateData,
): Promise<IToken | null> {
try {
const Token = mongoose.models.Token;
return await Token.findOneAndUpdate(query, updateData, { new: true });
} catch (error) {
logger.debug('An error occurred while updating token:', error);
throw error;
}
}
/**
* Deletes all Token documents that match the provided token, user ID, or email.
*/
async function deleteTokens(query: TokenQuery): Promise<TokenDeleteResult> {
try {
const Token = mongoose.models.Token;
const conditions = [];
if (query.userId !== undefined) {
conditions.push({ userId: query.userId });
}
if (query.token !== undefined) {
conditions.push({ token: query.token });
}
if (query.email !== undefined) {
conditions.push({ email: query.email });
}
if (query.identifier !== undefined) {
conditions.push({ identifier: query.identifier });
}
/**
* If no conditions are specified, throw an error to prevent accidental deletion of all tokens
*/
if (conditions.length === 0) {
throw new Error('At least one query parameter must be provided');
}
return await Token.deleteMany({
$or: conditions,
});
} catch (error) {
logger.debug('An error occurred while deleting tokens:', error);
throw error;
}
}
/**
* Finds a Token document that matches the provided query.
*/
async function findToken(query: TokenQuery): Promise<IToken | null> {
try {
const Token = mongoose.models.Token;
const conditions = [];
if (query.userId) {
conditions.push({ userId: query.userId });
}
if (query.token) {
conditions.push({ token: query.token });
}
if (query.email) {
conditions.push({ email: query.email });
}
if (query.identifier) {
conditions.push({ identifier: query.identifier });
}
const token = await Token.findOne({
$and: conditions,
}).lean();
return token as IToken | null;
} catch (error) {
logger.debug('An error occurred while finding token:', error);
throw error;
}
}
// Return all methods
return {
findToken,
createToken,
updateToken,
deleteTokens,
};
}
export type TokenMethods = ReturnType<typeof createTokenMethods>;