🔒 refactor: Optimize Email Domain Validation in OpenID, SAML, and Social Logins (#9567)

* refactor: Optimize Email Domain Validation in OpenID, SAML, and Social Login Strategies

    - Implemented email domain validation for user authentication in OpenID and SAML strategies, ensuring only allowed domains are processed.
    - Adjusted error messages for clarity and consistency across authentication methods.
    - Refactored social login to validate email domains before checking for existing users, improving registration flow.

* refactor: Email Domain Validation in LDAP and Social Login Strategies
This commit is contained in:
Danny Avila 2025-09-11 01:01:58 -04:00 committed by GitHub
parent 5676976564
commit d91f34dd42
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 78 additions and 60 deletions

View file

@ -3,12 +3,12 @@ const jwt = require('jsonwebtoken');
const { webcrypto } = require('node:crypto'); const { webcrypto } = require('node:crypto');
const { logger } = require('@librechat/data-schemas'); const { logger } = require('@librechat/data-schemas');
const { isEnabled, checkEmailConfig } = require('@librechat/api'); const { isEnabled, checkEmailConfig } = require('@librechat/api');
const { SystemRoles, errorsToString } = require('librechat-data-provider'); const { ErrorTypes, SystemRoles, errorsToString } = require('librechat-data-provider');
const { const {
findUser, findUser,
findToken,
createUser, createUser,
updateUser, updateUser,
findToken,
countUsers, countUsers,
getUserById, getUserById,
findSession, findSession,
@ -181,6 +181,14 @@ const registerUser = async (user, additionalData = {}) => {
let newUserId; let newUserId;
try { try {
const appConfig = await getAppConfig();
if (!isEmailDomainAllowed(email, appConfig?.registration?.allowedDomains)) {
const errorMessage =
'The email address provided cannot be used. Please use a different email address.';
logger.error(`[registerUser] [Registration not allowed] [Email: ${user.email}]`);
return { status: 403, message: errorMessage };
}
const existingUser = await findUser({ email }, 'email _id'); const existingUser = await findUser({ email }, 'email _id');
if (existingUser) { if (existingUser) {
@ -195,14 +203,6 @@ const registerUser = async (user, additionalData = {}) => {
return { status: 200, message: genericVerificationMessage }; return { status: 200, message: genericVerificationMessage };
} }
const appConfig = await getAppConfig({ role: user.role });
if (!isEmailDomainAllowed(email, appConfig?.registration?.allowedDomains)) {
const errorMessage =
'The email address provided cannot be used. Please use a different email address.';
logger.error(`[registerUser] [Registration not allowed] [Email: ${user.email}]`);
return { status: 403, message: errorMessage };
}
//determine if this is the first registered user (not counting anonymous_user) //determine if this is the first registered user (not counting anonymous_user)
const isFirstRegisteredUser = (await countUsers()) === 0; const isFirstRegisteredUser = (await countUsers()) === 0;
@ -252,6 +252,13 @@ const registerUser = async (user, additionalData = {}) => {
*/ */
const requestPasswordReset = async (req) => { const requestPasswordReset = async (req) => {
const { email } = req.body; const { email } = req.body;
const appConfig = await getAppConfig();
if (!isEmailDomainAllowed(email, appConfig?.registration?.allowedDomains)) {
const error = new Error(ErrorTypes.AUTH_FAILED);
error.code = ErrorTypes.AUTH_FAILED;
error.message = 'Email domain not allowed';
return error;
}
const user = await findUser({ email }, 'email _id'); const user = await findUser({ email }, 'email _id');
const emailEnabled = checkEmailConfig(); const emailEnabled = checkEmailConfig();

View file

@ -122,17 +122,17 @@ const ldapLogin = new LdapStrategy(ldapOptions, async (userinfo, done) => {
); );
} }
const appConfig = await getAppConfig();
if (!isEmailDomainAllowed(mail, appConfig?.registration?.allowedDomains)) {
logger.error(
`[LDAP Strategy] Authentication blocked - email domain not allowed [Email: ${mail}]`,
);
return done(null, false, { message: 'Email domain not allowed' });
}
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',

View file

@ -340,6 +340,19 @@ async function setupOpenId() {
async (tokenset, done) => { async (tokenset, done) => {
try { try {
const claims = tokenset.claims(); const claims = tokenset.claims();
const userinfo = {
...claims,
...(await getUserInfo(openidConfig, tokenset.access_token, claims.sub)),
};
const appConfig = await getAppConfig();
if (!isEmailDomainAllowed(userinfo.email, appConfig?.registration?.allowedDomains)) {
logger.error(
`[OpenID Strategy] Authentication blocked - email domain not allowed [Email: ${userinfo.email}]`,
);
return done(null, false, { message: 'Email domain not allowed' });
}
const result = await findOpenIDUser({ const result = await findOpenIDUser({
openidId: claims.sub, openidId: claims.sub,
email: claims.email, email: claims.email,
@ -354,10 +367,7 @@ async function setupOpenId() {
message: ErrorTypes.AUTH_FAILED, message: ErrorTypes.AUTH_FAILED,
}); });
} }
const userinfo = {
...claims,
...(await getUserInfo(openidConfig, tokenset.access_token, claims.sub)),
};
const fullName = getFullName(userinfo); const fullName = getFullName(userinfo);
if (requiredRole) { if (requiredRole) {
@ -399,15 +409,7 @@ async function setupOpenId() {
); );
} }
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,

View file

@ -193,16 +193,25 @@ async function setupSaml() {
logger.info(`[samlStrategy] SAML authentication received for NameID: ${profile.nameID}`); logger.info(`[samlStrategy] SAML authentication received for NameID: ${profile.nameID}`);
logger.debug('[samlStrategy] SAML profile:', profile); logger.debug('[samlStrategy] SAML profile:', profile);
const userEmail = getEmail(profile) || '';
const appConfig = await getAppConfig();
if (!isEmailDomainAllowed(userEmail, appConfig?.registration?.allowedDomains)) {
logger.error(
`[SAML Strategy] Authentication blocked - email domain not allowed [Email: ${userEmail}]`,
);
return done(null, false, { message: 'Email domain not allowed' });
}
let user = await findUser({ samlId: profile.nameID }); let user = await findUser({ samlId: profile.nameID });
logger.info( logger.info(
`[samlStrategy] User ${user ? 'found' : 'not found'} with SAML ID: ${profile.nameID}`, `[samlStrategy] User ${user ? 'found' : 'not found'} with SAML ID: ${profile.nameID}`,
); );
if (!user) { if (!user) {
const email = getEmail(profile) || ''; user = await findUser({ email: userEmail });
user = await findUser({ email });
logger.info( logger.info(
`[samlStrategy] User ${user ? 'found' : 'not found'} with email: ${profile.email}`, `[samlStrategy] User ${user ? 'found' : 'not found'} with email: ${userEmail}`,
); );
} }
@ -221,16 +230,7 @@ async function setupSaml() {
getUserName(profile) || getGivenName(profile) || getEmail(profile), getUserName(profile) || getGivenName(profile) || getEmail(profile),
); );
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,

View file

@ -15,19 +15,19 @@ const socialLogin =
}); });
const appConfig = await getAppConfig(); const appConfig = await getAppConfig();
const existingUser = await findUser({ email: email.trim() });
const ALLOW_SOCIAL_REGISTRATION = isEnabled(process.env.ALLOW_SOCIAL_REGISTRATION);
if (!existingUser && !isEmailDomainAllowed(email, appConfig?.registration?.allowedDomains)) { if (!isEmailDomainAllowed(email, appConfig?.registration?.allowedDomains)) {
logger.error( logger.error(
`[${provider}Login] Registration blocked - email domain not allowed [Email: ${email}]`, `[${provider}Login] Authentication blocked - email domain not allowed [Email: ${email}]`,
); );
const error = new Error(ErrorTypes.AUTH_FAILED); const error = new Error(ErrorTypes.AUTH_FAILED);
error.code = ErrorTypes.AUTH_FAILED; error.code = ErrorTypes.AUTH_FAILED;
error.message = 'Email domain not allowed for registration'; error.message = 'Email domain not allowed';
return cb(error); return cb(error);
} }
const existingUser = await findUser({ email: email.trim() });
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);
@ -41,7 +41,17 @@ const socialLogin =
return cb(error); return cb(error);
} }
if (ALLOW_SOCIAL_REGISTRATION) { const ALLOW_SOCIAL_REGISTRATION = isEnabled(process.env.ALLOW_SOCIAL_REGISTRATION);
if (!ALLOW_SOCIAL_REGISTRATION) {
logger.error(
`[${provider}Login] Registration blocked - social registration is disabled [Email: ${email}]`,
);
const error = new Error(ErrorTypes.AUTH_FAILED);
error.code = ErrorTypes.AUTH_FAILED;
error.message = 'Social registration is disabled';
return cb(error);
}
const newUser = await createSocialUser({ const newUser = await createSocialUser({
email, email,
avatarUrl, avatarUrl,
@ -54,7 +64,6 @@ const socialLogin =
appConfig, appConfig,
}); });
return cb(null, newUser); return cb(null, newUser);
}
} catch (err) { } catch (err) {
logger.error(`[${provider}Login]`, err); logger.error(`[${provider}Login]`, err);
return cb(err); return cb(err);