mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-01-01 16:18:51 +01:00
🔒 fix: Provider Validation for Social, OpenID, SAML, and LDAP Logins (#8999)
* fix: social login provider crossover * feat: Enhance OpenID login handling and add tests for provider validation * refactor: authentication error handling to use ErrorTypes.AUTH_FAILED enum * refactor: update authentication error handling in LDAP and SAML strategies to use ErrorTypes.AUTH_FAILED enum * ci: Add validation for login with existing email and different provider in SAML strategy chore: Add logging for existing users with different providers in LDAP, SAML, and Social Login strategies
This commit is contained in:
parent
04d74a7e07
commit
1ccac58403
18 changed files with 314 additions and 125 deletions
83
packages/api/src/middleware/error.ts
Normal file
83
packages/api/src/middleware/error.ts
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
import { logger } from '@librechat/data-schemas';
|
||||
import { ErrorTypes } from 'librechat-data-provider';
|
||||
import type { NextFunction, Request, Response } from 'express';
|
||||
import type { MongoServerError, ValidationError, CustomError } from '~/types';
|
||||
|
||||
const handleDuplicateKeyError = (err: MongoServerError, res: Response) => {
|
||||
logger.warn('Duplicate key error: ' + (err.errmsg || err.message));
|
||||
const field = err.keyValue ? `${JSON.stringify(Object.keys(err.keyValue))}` : 'unknown';
|
||||
const code = 409;
|
||||
res
|
||||
.status(code)
|
||||
.send({ messages: `An document with that ${field} already exists.`, fields: field });
|
||||
};
|
||||
|
||||
const handleValidationError = (err: ValidationError, res: Response) => {
|
||||
logger.error('Validation error:', err.errors);
|
||||
const errorMessages = Object.values(err.errors).map((el) => el.message);
|
||||
const fields = `${JSON.stringify(Object.values(err.errors).map((el) => el.path))}`;
|
||||
const code = 400;
|
||||
const messages =
|
||||
errorMessages.length > 1
|
||||
? `${JSON.stringify(errorMessages.join(' '))}`
|
||||
: `${JSON.stringify(errorMessages)}`;
|
||||
|
||||
res.status(code).send({ messages, fields });
|
||||
};
|
||||
|
||||
/** Type guard for ValidationError */
|
||||
function isValidationError(err: unknown): err is ValidationError {
|
||||
return err !== null && typeof err === 'object' && 'name' in err && err.name === 'ValidationError';
|
||||
}
|
||||
|
||||
/** Type guard for MongoServerError (duplicate key) */
|
||||
function isMongoServerError(err: unknown): err is MongoServerError {
|
||||
return err !== null && typeof err === 'object' && 'code' in err && err.code === 11000;
|
||||
}
|
||||
|
||||
/** Type guard for CustomError with statusCode and body */
|
||||
function isCustomError(err: unknown): err is CustomError {
|
||||
return err !== null && typeof err === 'object' && 'statusCode' in err && 'body' in err;
|
||||
}
|
||||
|
||||
export const ErrorController = (
|
||||
err: Error | CustomError,
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction,
|
||||
): Response | void => {
|
||||
try {
|
||||
if (!err) {
|
||||
return next();
|
||||
}
|
||||
const error = err as CustomError;
|
||||
|
||||
if (
|
||||
(error.message === ErrorTypes.AUTH_FAILED || error.code === ErrorTypes.AUTH_FAILED) &&
|
||||
req.originalUrl &&
|
||||
req.originalUrl.includes('/oauth/') &&
|
||||
req.originalUrl.includes('/callback')
|
||||
) {
|
||||
const domain = process.env.DOMAIN_CLIENT || 'http://localhost:3080';
|
||||
return res.redirect(`${domain}/login?redirect=false&error=${ErrorTypes.AUTH_FAILED}`);
|
||||
}
|
||||
|
||||
if (isValidationError(error)) {
|
||||
return handleValidationError(error, res);
|
||||
}
|
||||
|
||||
if (isMongoServerError(error)) {
|
||||
return handleDuplicateKeyError(error, res);
|
||||
}
|
||||
|
||||
if (isCustomError(error) && error.statusCode && error.body) {
|
||||
return res.status(error.statusCode).send(error.body);
|
||||
}
|
||||
|
||||
logger.error('ErrorController => error', err);
|
||||
return res.status(500).send('An unknown error occurred.');
|
||||
} catch (processingError) {
|
||||
logger.error('ErrorController => processing error', processingError);
|
||||
return res.status(500).send('Processing error in ErrorController.');
|
||||
}
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue