mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-09-21 21:50:49 +02:00
🔒 fix: Email Domain Validation Order and Coverage (#9566)
This commit is contained in:
parent
85aa3e7d9c
commit
5676976564
6 changed files with 69 additions and 15 deletions
|
@ -11,18 +11,25 @@ const { getAppConfig } = require('~/server/services/Config');
|
||||||
* @param {Object} res - Express response object.
|
* @param {Object} res - Express response object.
|
||||||
* @param {Function} next - Next middleware function.
|
* @param {Function} next - Next middleware function.
|
||||||
*
|
*
|
||||||
* @returns {Promise<function|Object>} - Returns a Promise which when resolved calls next middleware if the domain's email is allowed
|
* @returns {Promise<void>} - Calls next middleware if the domain's email is allowed, otherwise redirects to login
|
||||||
*/
|
*/
|
||||||
const checkDomainAllowed = async (req, res, next = () => {}) => {
|
const checkDomainAllowed = async (req, res, next) => {
|
||||||
const email = req?.user?.email;
|
try {
|
||||||
const appConfig = await getAppConfig({
|
const email = req?.user?.email;
|
||||||
role: req?.user?.role,
|
const appConfig = await getAppConfig({
|
||||||
});
|
role: req?.user?.role,
|
||||||
if (email && !isEmailDomainAllowed(email, appConfig?.registration?.allowedDomains)) {
|
});
|
||||||
logger.error(`[Social Login] [Social Login not allowed] [Email: ${email}]`);
|
|
||||||
return res.redirect('/login');
|
if (email && !isEmailDomainAllowed(email, appConfig?.registration?.allowedDomains)) {
|
||||||
} else {
|
logger.error(`[Social Login] [Social Login not allowed] [Email: ${email}]`);
|
||||||
return next();
|
res.redirect('/login');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('[checkDomainAllowed] Error checking domain:', error);
|
||||||
|
res.redirect('/login');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -26,9 +26,12 @@ const domains = {
|
||||||
router.use(logHeaders);
|
router.use(logHeaders);
|
||||||
router.use(loginLimiter);
|
router.use(loginLimiter);
|
||||||
|
|
||||||
const oauthHandler = async (req, res) => {
|
const oauthHandler = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
await checkDomainAllowed(req, res);
|
if (res.headersSent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await checkBan(req, res);
|
await checkBan(req, res);
|
||||||
if (req.banned) {
|
if (req.banned) {
|
||||||
return;
|
return;
|
||||||
|
@ -46,6 +49,7 @@ const oauthHandler = async (req, res) => {
|
||||||
res.redirect(domains.client);
|
res.redirect(domains.client);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error('Error in setting authentication tokens:', err);
|
logger.error('Error in setting authentication tokens:', err);
|
||||||
|
next(err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -79,6 +83,7 @@ router.get(
|
||||||
scope: ['openid', 'profile', 'email'],
|
scope: ['openid', 'profile', 'email'],
|
||||||
}),
|
}),
|
||||||
setBalanceConfig,
|
setBalanceConfig,
|
||||||
|
checkDomainAllowed,
|
||||||
oauthHandler,
|
oauthHandler,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -104,6 +109,7 @@ router.get(
|
||||||
profileFields: ['id', 'email', 'name'],
|
profileFields: ['id', 'email', 'name'],
|
||||||
}),
|
}),
|
||||||
setBalanceConfig,
|
setBalanceConfig,
|
||||||
|
checkDomainAllowed,
|
||||||
oauthHandler,
|
oauthHandler,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -125,6 +131,7 @@ router.get(
|
||||||
session: false,
|
session: false,
|
||||||
}),
|
}),
|
||||||
setBalanceConfig,
|
setBalanceConfig,
|
||||||
|
checkDomainAllowed,
|
||||||
oauthHandler,
|
oauthHandler,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -148,6 +155,7 @@ router.get(
|
||||||
scope: ['user:email', 'read:user'],
|
scope: ['user:email', 'read:user'],
|
||||||
}),
|
}),
|
||||||
setBalanceConfig,
|
setBalanceConfig,
|
||||||
|
checkDomainAllowed,
|
||||||
oauthHandler,
|
oauthHandler,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -171,6 +179,7 @@ router.get(
|
||||||
scope: ['identify', 'email'],
|
scope: ['identify', 'email'],
|
||||||
}),
|
}),
|
||||||
setBalanceConfig,
|
setBalanceConfig,
|
||||||
|
checkDomainAllowed,
|
||||||
oauthHandler,
|
oauthHandler,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -192,6 +201,7 @@ router.post(
|
||||||
session: false,
|
session: false,
|
||||||
}),
|
}),
|
||||||
setBalanceConfig,
|
setBalanceConfig,
|
||||||
|
checkDomainAllowed,
|
||||||
oauthHandler,
|
oauthHandler,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ const { logger } = require('@librechat/data-schemas');
|
||||||
const { isEnabled, getBalanceConfig } = require('@librechat/api');
|
const { isEnabled, getBalanceConfig } = require('@librechat/api');
|
||||||
const { SystemRoles, ErrorTypes } = require('librechat-data-provider');
|
const { SystemRoles, ErrorTypes } = require('librechat-data-provider');
|
||||||
const { createUser, findUser, updateUser, countUsers } = require('~/models');
|
const { createUser, findUser, updateUser, countUsers } = require('~/models');
|
||||||
|
const { isEmailDomainAllowed } = require('~/server/services/domains');
|
||||||
const { getAppConfig } = require('~/server/services/Config');
|
const { getAppConfig } = require('~/server/services/Config');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -124,6 +125,15 @@ const ldapLogin = new LdapStrategy(ldapOptions, async (userinfo, done) => {
|
||||||
if (!user) {
|
if (!user) {
|
||||||
const isFirstRegisteredUser = (await countUsers()) === 0;
|
const isFirstRegisteredUser = (await countUsers()) === 0;
|
||||||
const role = isFirstRegisteredUser ? SystemRoles.ADMIN : SystemRoles.USER;
|
const role = isFirstRegisteredUser ? SystemRoles.ADMIN : SystemRoles.USER;
|
||||||
|
const appConfig = await getAppConfig({ role });
|
||||||
|
|
||||||
|
if (!isEmailDomainAllowed(mail, appConfig?.registration?.allowedDomains)) {
|
||||||
|
logger.error(
|
||||||
|
`[LDAP Strategy] Registration blocked - email domain not allowed [Email: ${mail}]`,
|
||||||
|
);
|
||||||
|
return done(null, false, { message: 'Email domain not allowed for registration' });
|
||||||
|
}
|
||||||
|
|
||||||
user = {
|
user = {
|
||||||
provider: 'ldap',
|
provider: 'ldap',
|
||||||
ldapId,
|
ldapId,
|
||||||
|
@ -133,7 +143,6 @@ const ldapLogin = new LdapStrategy(ldapOptions, async (userinfo, done) => {
|
||||||
name: fullName,
|
name: fullName,
|
||||||
role,
|
role,
|
||||||
};
|
};
|
||||||
const appConfig = await getAppConfig({ role: user?.role });
|
|
||||||
const balanceConfig = getBalanceConfig(appConfig);
|
const balanceConfig = getBalanceConfig(appConfig);
|
||||||
const userId = await createUser(user, balanceConfig);
|
const userId = await createUser(user, balanceConfig);
|
||||||
user._id = userId;
|
user._id = userId;
|
||||||
|
|
|
@ -15,6 +15,7 @@ const {
|
||||||
getBalanceConfig,
|
getBalanceConfig,
|
||||||
} = require('@librechat/api');
|
} = require('@librechat/api');
|
||||||
const { getStrategyFunctions } = require('~/server/services/Files/strategies');
|
const { getStrategyFunctions } = require('~/server/services/Files/strategies');
|
||||||
|
const { isEmailDomainAllowed } = require('~/server/services/domains');
|
||||||
const { findUser, createUser, updateUser } = require('~/models');
|
const { findUser, createUser, updateUser } = require('~/models');
|
||||||
const { getAppConfig } = require('~/server/services/Config');
|
const { getAppConfig } = require('~/server/services/Config');
|
||||||
const getLogStores = require('~/cache/getLogStores');
|
const getLogStores = require('~/cache/getLogStores');
|
||||||
|
@ -400,6 +401,13 @@ async function setupOpenId() {
|
||||||
|
|
||||||
const appConfig = await getAppConfig();
|
const appConfig = await getAppConfig();
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
if (!isEmailDomainAllowed(userinfo.email, appConfig?.registration?.allowedDomains)) {
|
||||||
|
logger.error(
|
||||||
|
`[OpenID Strategy] Registration blocked - email domain not allowed [Email: ${userinfo.email}]`,
|
||||||
|
);
|
||||||
|
return done(null, false, { message: 'Email domain not allowed for registration' });
|
||||||
|
}
|
||||||
|
|
||||||
user = {
|
user = {
|
||||||
provider: 'openid',
|
provider: 'openid',
|
||||||
openidId: userinfo.sub,
|
openidId: userinfo.sub,
|
||||||
|
|
|
@ -7,6 +7,7 @@ const { ErrorTypes } = require('librechat-data-provider');
|
||||||
const { hashToken, logger } = require('@librechat/data-schemas');
|
const { hashToken, logger } = require('@librechat/data-schemas');
|
||||||
const { Strategy: SamlStrategy } = require('@node-saml/passport-saml');
|
const { Strategy: SamlStrategy } = require('@node-saml/passport-saml');
|
||||||
const { getStrategyFunctions } = require('~/server/services/Files/strategies');
|
const { getStrategyFunctions } = require('~/server/services/Files/strategies');
|
||||||
|
const { isEmailDomainAllowed } = require('~/server/services/domains');
|
||||||
const { findUser, createUser, updateUser } = require('~/models');
|
const { findUser, createUser, updateUser } = require('~/models');
|
||||||
const { getAppConfig } = require('~/server/services/Config');
|
const { getAppConfig } = require('~/server/services/Config');
|
||||||
const paths = require('~/config/paths');
|
const paths = require('~/config/paths');
|
||||||
|
@ -222,11 +223,19 @@ async function setupSaml() {
|
||||||
|
|
||||||
const appConfig = await getAppConfig();
|
const appConfig = await getAppConfig();
|
||||||
if (!user) {
|
if (!user) {
|
||||||
|
const userEmail = getEmail(profile) || '';
|
||||||
|
if (!isEmailDomainAllowed(userEmail, appConfig?.registration?.allowedDomains)) {
|
||||||
|
logger.error(
|
||||||
|
`[SAML Strategy] Registration blocked - email domain not allowed [Email: ${userEmail}]`,
|
||||||
|
);
|
||||||
|
return done(null, false, { message: 'Email domain not allowed for registration' });
|
||||||
|
}
|
||||||
|
|
||||||
user = {
|
user = {
|
||||||
provider: 'saml',
|
provider: 'saml',
|
||||||
samlId: profile.nameID,
|
samlId: profile.nameID,
|
||||||
username,
|
username,
|
||||||
email: getEmail(profile) || '',
|
email: userEmail,
|
||||||
emailVerified: true,
|
emailVerified: true,
|
||||||
name: fullName,
|
name: fullName,
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,6 +2,7 @@ const { isEnabled } = require('@librechat/api');
|
||||||
const { logger } = require('@librechat/data-schemas');
|
const { logger } = require('@librechat/data-schemas');
|
||||||
const { ErrorTypes } = require('librechat-data-provider');
|
const { ErrorTypes } = require('librechat-data-provider');
|
||||||
const { createSocialUser, handleExistingUser } = require('./process');
|
const { createSocialUser, handleExistingUser } = require('./process');
|
||||||
|
const { isEmailDomainAllowed } = require('~/server/services/domains');
|
||||||
const { getAppConfig } = require('~/server/services/Config');
|
const { getAppConfig } = require('~/server/services/Config');
|
||||||
const { findUser } = require('~/models');
|
const { findUser } = require('~/models');
|
||||||
|
|
||||||
|
@ -17,6 +18,16 @@ const socialLogin =
|
||||||
const existingUser = await findUser({ email: email.trim() });
|
const existingUser = await findUser({ email: email.trim() });
|
||||||
const ALLOW_SOCIAL_REGISTRATION = isEnabled(process.env.ALLOW_SOCIAL_REGISTRATION);
|
const ALLOW_SOCIAL_REGISTRATION = isEnabled(process.env.ALLOW_SOCIAL_REGISTRATION);
|
||||||
|
|
||||||
|
if (!existingUser && !isEmailDomainAllowed(email, appConfig?.registration?.allowedDomains)) {
|
||||||
|
logger.error(
|
||||||
|
`[${provider}Login] Registration blocked - email domain not allowed [Email: ${email}]`,
|
||||||
|
);
|
||||||
|
const error = new Error(ErrorTypes.AUTH_FAILED);
|
||||||
|
error.code = ErrorTypes.AUTH_FAILED;
|
||||||
|
error.message = 'Email domain not allowed for registration';
|
||||||
|
return cb(error);
|
||||||
|
}
|
||||||
|
|
||||||
if (existingUser?.provider === provider) {
|
if (existingUser?.provider === provider) {
|
||||||
await handleExistingUser(existingUser, avatarUrl, appConfig);
|
await handleExistingUser(existingUser, avatarUrl, appConfig);
|
||||||
return cb(null, existingUser);
|
return cb(null, existingUser);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue