mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 08:50:15 +01:00
🔒refactor: social login and remove direct user model access in strategies (#2946)
* refactor: checking `ALLOW_SOCIAL_REGISTRATION` with `isEnabled` * feat: Add findUserByEmail function to UserService This commit adds a new function, , to the module. This function retrieves a user document from the database based on the provided email. It returns the user document if found, otherwise it returns null. If there is a problem during user retrieval, an error is thrown. * refactor: add socialLogin to remove repetitive code
This commit is contained in:
parent
5452d4c20c
commit
b7fef6958b
6 changed files with 104 additions and 143 deletions
|
|
@ -166,6 +166,23 @@ const checkUserKeyExpiry = (expiresAt, endpoint) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a user document from the database based on the provided email.
|
||||||
|
* @async
|
||||||
|
* @param {string} email - The email of the user to find.
|
||||||
|
* @returns {Promise<Object|null>} The user document if found, otherwise null.
|
||||||
|
* @throws {Error} Throws an error if there is a problem during user retrieval.
|
||||||
|
*/
|
||||||
|
const findUserByEmail = async (email) => {
|
||||||
|
try {
|
||||||
|
const user = await User.findOne({ email });
|
||||||
|
return user;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`[findUserByEmail] Error occurred while finding user by email: ${email}`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
updateUserPluginsService,
|
updateUserPluginsService,
|
||||||
getUserKey,
|
getUserKey,
|
||||||
|
|
@ -174,4 +191,5 @@ module.exports = {
|
||||||
updateUserKey,
|
updateUserKey,
|
||||||
deleteUserKey,
|
deleteUserKey,
|
||||||
checkUserKeyExpiry,
|
checkUserKeyExpiry,
|
||||||
|
findUserByEmail,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,8 @@
|
||||||
const { Strategy: DiscordStrategy } = require('passport-discord');
|
const { Strategy: DiscordStrategy } = require('passport-discord');
|
||||||
const { createNewUser, handleExistingUser } = require('./process');
|
const socialLogin = require('./socialLogin');
|
||||||
const { logger } = require('~/config');
|
|
||||||
const User = require('~/models/User');
|
|
||||||
|
|
||||||
const discordLogin = async (accessToken, refreshToken, profile, cb) => {
|
const getProfileDetails = (profile) => {
|
||||||
try {
|
|
||||||
const email = profile.email;
|
|
||||||
const discordId = profile.id;
|
|
||||||
|
|
||||||
// TODO: remove direct access of User model
|
|
||||||
const oldUser = await User.findOne({ email });
|
|
||||||
const ALLOW_SOCIAL_REGISTRATION =
|
|
||||||
process.env.ALLOW_SOCIAL_REGISTRATION?.toLowerCase() === 'true';
|
|
||||||
let avatarUrl;
|
let avatarUrl;
|
||||||
|
|
||||||
if (profile.avatar) {
|
if (profile.avatar) {
|
||||||
const format = profile.avatar.startsWith('a_') ? 'gif' : 'png';
|
const format = profile.avatar.startsWith('a_') ? 'gif' : 'png';
|
||||||
avatarUrl = `https://cdn.discordapp.com/avatars/${profile.id}/${profile.avatar}.${format}`;
|
avatarUrl = `https://cdn.discordapp.com/avatars/${profile.id}/${profile.avatar}.${format}`;
|
||||||
|
|
@ -22,29 +11,17 @@ const discordLogin = async (accessToken, refreshToken, profile, cb) => {
|
||||||
avatarUrl = `https://cdn.discordapp.com/embed/avatars/${defaultAvatarNum}.png`;
|
avatarUrl = `https://cdn.discordapp.com/embed/avatars/${defaultAvatarNum}.png`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldUser) {
|
return {
|
||||||
await handleExistingUser(oldUser, avatarUrl);
|
email: profile.email,
|
||||||
return cb(null, oldUser);
|
id: profile.id,
|
||||||
}
|
|
||||||
|
|
||||||
if (ALLOW_SOCIAL_REGISTRATION) {
|
|
||||||
const newUser = await createNewUser({
|
|
||||||
email,
|
|
||||||
avatarUrl,
|
avatarUrl,
|
||||||
provider: 'discord',
|
|
||||||
providerKey: 'discordId',
|
|
||||||
providerId: discordId,
|
|
||||||
username: profile.username,
|
username: profile.username,
|
||||||
name: profile.global_name,
|
name: profile.global_name,
|
||||||
});
|
};
|
||||||
return cb(null, newUser);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
logger.error('[discordLogin]', err);
|
|
||||||
return cb(err);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const discordLogin = socialLogin('discord', getProfileDetails);
|
||||||
|
|
||||||
module.exports = () =>
|
module.exports = () =>
|
||||||
new DiscordStrategy(
|
new DiscordStrategy(
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,15 @@
|
||||||
const FacebookStrategy = require('passport-facebook').Strategy;
|
const FacebookStrategy = require('passport-facebook').Strategy;
|
||||||
const { createNewUser, handleExistingUser } = require('./process');
|
const socialLogin = require('./socialLogin');
|
||||||
const { logger } = require('~/config');
|
|
||||||
const User = require('~/models/User');
|
|
||||||
|
|
||||||
const facebookLogin = async (accessToken, refreshToken, profile, cb) => {
|
const getProfileDetails = (profile) => ({
|
||||||
try {
|
email: profile.emails[0]?.value,
|
||||||
const email = profile.emails[0]?.value;
|
id: profile.id,
|
||||||
const facebookId = profile.id;
|
avatarUrl: profile.photos[0]?.value,
|
||||||
const oldUser = await User.findOne({ email });
|
|
||||||
const ALLOW_SOCIAL_REGISTRATION =
|
|
||||||
process.env.ALLOW_SOCIAL_REGISTRATION?.toLowerCase() === 'true';
|
|
||||||
const avatarUrl = profile.photos[0]?.value;
|
|
||||||
|
|
||||||
if (oldUser) {
|
|
||||||
await handleExistingUser(oldUser, avatarUrl);
|
|
||||||
return cb(null, oldUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ALLOW_SOCIAL_REGISTRATION) {
|
|
||||||
const newUser = await createNewUser({
|
|
||||||
email,
|
|
||||||
avatarUrl,
|
|
||||||
provider: 'facebook',
|
|
||||||
providerKey: 'facebookId',
|
|
||||||
providerId: facebookId,
|
|
||||||
username: profile.displayName,
|
username: profile.displayName,
|
||||||
name: profile.name?.givenName + ' ' + profile.name?.familyName,
|
name: profile.name?.givenName + ' ' + profile.name?.familyName,
|
||||||
});
|
});
|
||||||
return cb(null, newUser);
|
|
||||||
}
|
const facebookLogin = socialLogin('facebook', getProfileDetails);
|
||||||
} catch (err) {
|
|
||||||
logger.error('[facebookLogin]', err);
|
|
||||||
return cb(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = () =>
|
module.exports = () =>
|
||||||
new FacebookStrategy(
|
new FacebookStrategy(
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,16 @@
|
||||||
const { Strategy: GitHubStrategy } = require('passport-github2');
|
const { Strategy: GitHubStrategy } = require('passport-github2');
|
||||||
const { createNewUser, handleExistingUser } = require('./process');
|
const socialLogin = require('./socialLogin');
|
||||||
const { logger } = require('~/config');
|
|
||||||
const User = require('~/models/User');
|
|
||||||
|
|
||||||
const githubLogin = async (accessToken, refreshToken, profile, cb) => {
|
const getProfileDetails = (profile) => ({
|
||||||
try {
|
email: profile.emails[0].value,
|
||||||
const email = profile.emails[0].value;
|
id: profile.id,
|
||||||
const githubId = profile.id;
|
avatarUrl: profile.photos[0].value,
|
||||||
const oldUser = await User.findOne({ email });
|
|
||||||
const ALLOW_SOCIAL_REGISTRATION =
|
|
||||||
process.env.ALLOW_SOCIAL_REGISTRATION?.toLowerCase() === 'true';
|
|
||||||
const avatarUrl = profile.photos[0].value;
|
|
||||||
|
|
||||||
if (oldUser) {
|
|
||||||
await handleExistingUser(oldUser, avatarUrl);
|
|
||||||
return cb(null, oldUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ALLOW_SOCIAL_REGISTRATION) {
|
|
||||||
const newUser = await createNewUser({
|
|
||||||
email,
|
|
||||||
avatarUrl,
|
|
||||||
provider: 'github',
|
|
||||||
providerKey: 'githubId',
|
|
||||||
providerId: githubId,
|
|
||||||
username: profile.username,
|
username: profile.username,
|
||||||
name: profile.displayName,
|
name: profile.displayName,
|
||||||
emailVerified: profile.emails[0].verified,
|
emailVerified: profile.emails[0].verified,
|
||||||
});
|
});
|
||||||
return cb(null, newUser);
|
|
||||||
}
|
const githubLogin = socialLogin('github', getProfileDetails);
|
||||||
} catch (err) {
|
|
||||||
logger.error('[githubLogin]', err);
|
|
||||||
return cb(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = () =>
|
module.exports = () =>
|
||||||
new GitHubStrategy(
|
new GitHubStrategy(
|
||||||
|
|
|
||||||
|
|
@ -1,40 +1,16 @@
|
||||||
const { Strategy: GoogleStrategy } = require('passport-google-oauth20');
|
const { Strategy: GoogleStrategy } = require('passport-google-oauth20');
|
||||||
const { createNewUser, handleExistingUser } = require('./process');
|
const socialLogin = require('./socialLogin');
|
||||||
const { logger } = require('~/config');
|
|
||||||
const User = require('~/models/User');
|
|
||||||
|
|
||||||
const googleLogin = async (accessToken, refreshToken, profile, cb) => {
|
const getProfileDetails = (profile) => ({
|
||||||
try {
|
email: profile.emails[0].value,
|
||||||
const email = profile.emails[0].value;
|
id: profile.id,
|
||||||
const googleId = profile.id;
|
avatarUrl: profile.photos[0].value,
|
||||||
const oldUser = await User.findOne({ email });
|
|
||||||
const ALLOW_SOCIAL_REGISTRATION =
|
|
||||||
process.env.ALLOW_SOCIAL_REGISTRATION?.toLowerCase() === 'true';
|
|
||||||
const avatarUrl = profile.photos[0].value;
|
|
||||||
|
|
||||||
if (oldUser) {
|
|
||||||
await handleExistingUser(oldUser, avatarUrl);
|
|
||||||
return cb(null, oldUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ALLOW_SOCIAL_REGISTRATION) {
|
|
||||||
const newUser = await createNewUser({
|
|
||||||
email,
|
|
||||||
avatarUrl,
|
|
||||||
provider: 'google',
|
|
||||||
providerKey: 'googleId',
|
|
||||||
providerId: googleId,
|
|
||||||
username: profile.name.givenName,
|
username: profile.name.givenName,
|
||||||
name: `${profile.name.givenName} ${profile.name.familyName}`,
|
name: `${profile.name.givenName} ${profile.name.familyName}`,
|
||||||
emailVerified: profile.emails[0].verified,
|
emailVerified: profile.emails[0].verified,
|
||||||
});
|
});
|
||||||
return cb(null, newUser);
|
|
||||||
}
|
const googleLogin = socialLogin('google', getProfileDetails);
|
||||||
} catch (err) {
|
|
||||||
logger.error('[googleLogin]', err);
|
|
||||||
return cb(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = () =>
|
module.exports = () =>
|
||||||
new GoogleStrategy(
|
new GoogleStrategy(
|
||||||
|
|
|
||||||
38
api/strategies/socialLogin.js
Normal file
38
api/strategies/socialLogin.js
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
const { createNewUser, handleExistingUser } = require('./process');
|
||||||
|
const { logger } = require('~/config');
|
||||||
|
const { findUserByEmail } = require('~/server/services/UserService');
|
||||||
|
const { isEnabled } = require('~/server/utils');
|
||||||
|
|
||||||
|
const socialLogin =
|
||||||
|
(provider, getProfileDetails) => async (accessToken, refreshToken, profile, cb) => {
|
||||||
|
try {
|
||||||
|
const { email, id, avatarUrl, username, name, emailVerified } = getProfileDetails(profile);
|
||||||
|
|
||||||
|
const oldUser = await findUserByEmail(email);
|
||||||
|
const ALLOW_SOCIAL_REGISTRATION = isEnabled(process.env.ALLOW_SOCIAL_REGISTRATION);
|
||||||
|
|
||||||
|
if (oldUser) {
|
||||||
|
await handleExistingUser(oldUser, avatarUrl);
|
||||||
|
return cb(null, oldUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ALLOW_SOCIAL_REGISTRATION) {
|
||||||
|
const newUser = await createNewUser({
|
||||||
|
email,
|
||||||
|
avatarUrl,
|
||||||
|
provider,
|
||||||
|
providerKey: `${provider}Id`,
|
||||||
|
providerId: id,
|
||||||
|
username,
|
||||||
|
name,
|
||||||
|
emailVerified,
|
||||||
|
});
|
||||||
|
return cb(null, newUser);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(`[${provider}Login]`, err);
|
||||||
|
return cb(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = socialLogin;
|
||||||
Loading…
Add table
Add a link
Reference in a new issue