🔒 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 { logger } = require('@librechat/data-schemas');
const { isEnabled, checkEmailConfig } = require('@librechat/api');
const { SystemRoles, errorsToString } = require('librechat-data-provider');
const { ErrorTypes, SystemRoles, errorsToString } = require('librechat-data-provider');
const {
findUser,
findToken,
createUser,
updateUser,
findToken,
countUsers,
getUserById,
findSession,
@ -181,6 +181,14 @@ const registerUser = async (user, additionalData = {}) => {
let newUserId;
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');
if (existingUser) {
@ -195,14 +203,6 @@ const registerUser = async (user, additionalData = {}) => {
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)
const isFirstRegisteredUser = (await countUsers()) === 0;
@ -252,6 +252,13 @@ const registerUser = async (user, additionalData = {}) => {
*/
const requestPasswordReset = async (req) => {
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 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) {
const isFirstRegisteredUser = (await countUsers()) === 0;
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 = {
provider: 'ldap',

View file

@ -340,6 +340,19 @@ async function setupOpenId() {
async (tokenset, done) => {
try {
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({
openidId: claims.sub,
email: claims.email,
@ -354,10 +367,7 @@ async function setupOpenId() {
message: ErrorTypes.AUTH_FAILED,
});
}
const userinfo = {
...claims,
...(await getUserInfo(openidConfig, tokenset.access_token, claims.sub)),
};
const fullName = getFullName(userinfo);
if (requiredRole) {
@ -399,15 +409,7 @@ async function setupOpenId() {
);
}
const appConfig = await getAppConfig();
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 = {
provider: 'openid',
openidId: userinfo.sub,

View file

@ -193,16 +193,25 @@ async function setupSaml() {
logger.info(`[samlStrategy] SAML authentication received for NameID: ${profile.nameID}`);
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 });
logger.info(
`[samlStrategy] User ${user ? 'found' : 'not found'} with SAML ID: ${profile.nameID}`,
);
if (!user) {
const email = getEmail(profile) || '';
user = await findUser({ email });
user = await findUser({ email: userEmail });
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),
);
const appConfig = await getAppConfig();
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 = {
provider: 'saml',
samlId: profile.nameID,

View file

@ -15,19 +15,19 @@ const socialLogin =
});
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(
`[${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);
error.code = ErrorTypes.AUTH_FAILED;
error.message = 'Email domain not allowed for registration';
error.message = 'Email domain not allowed';
return cb(error);
}
const existingUser = await findUser({ email: email.trim() });
if (existingUser?.provider === provider) {
await handleExistingUser(existingUser, avatarUrl, appConfig);
return cb(null, existingUser);
@ -41,20 +41,29 @@ const socialLogin =
return cb(error);
}
if (ALLOW_SOCIAL_REGISTRATION) {
const newUser = await createSocialUser({
email,
avatarUrl,
provider,
providerKey: `${provider}Id`,
providerId: id,
username,
name,
emailVerified,
appConfig,
});
return cb(null, newUser);
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({
email,
avatarUrl,
provider,
providerKey: `${provider}Id`,
providerId: id,
username,
name,
emailVerified,
appConfig,
});
return cb(null, newUser);
} catch (err) {
logger.error(`[${provider}Login]`, err);
return cb(err);