Discord Login (#615)

* Add files via upload

* Create linode-setup.md

* Create cloudflare-setup.md

* Update cloudflare-setup.md

* Delete 4-linode.png

* Delete 3-linode.png

* Add files via upload

* Add files via upload

* Update cloudflare-setup.md

* Update linode-setup.md

* Rename cloudflare-setup.md to cloudflare.md

* Rename linode-setup.md to linode.md

* Update mkdocs.yml

* Update cloudflare.md

* Update linode.md

* Update README.md

* Update README.md

* Update linode.md

sentence in Italian

* v1

The frontend has been completed, along with the .env variables.

However, there is an issue of infinite loading thereafter.

* Fix email and remove deprecated GitHub passport

* Update user_auth_system.md

add How to Set Up a Github Authentication

* Update .env.example

Improved the comment above the GitHub client ID and secret.

* Update user_auth_system.md

* Update package.json

* Remove unnecessary passport GitHub package

* fixed conflicts

 fixed conflicts between Berry-13:main and danny-avila:main

in api/server/index.js 45:54

* Delete e -i HEAD~2

* (WIP) Discord Login

* Fix duplicate githubLoginEnabled

* .env.example restore

* Update user_auth_system.md

Discord Login

* Fix and new Feature

1. Added Discord login to .env.example.
2. Created Google, Github, and Discord icons in client\src\components\svg.
3. Added the social login option in the .env file; it fixes the ---or---. Check Discord for more information.

* fix Login.tsx and Registration.tsx

* Update user_auth_system.md

* Update .env.example

* Added OpenID Icon

* quick discord icon fix

* discord strategy fix

* remove comment
This commit is contained in:
Marco Beretta 2023-07-11 23:17:58 +02:00 committed by GitHub
parent c17c1488ca
commit 747e087cf5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 328 additions and 144 deletions

View file

@ -187,6 +187,9 @@ MEILI_MASTER_KEY=DrhYf7zENyR6AlUCKmnz0eYASOQdl6zxH7s7MKFSfFCt
# Allow Public Registration
ALLOW_REGISTRATION=true
# Allow Social Registration
ALLOW_SOCIAL_LOGIN=false
# JWT Secrets
JWT_SECRET=secret
JWT_REFRESH_SECRET=secret
@ -219,12 +222,20 @@ OPENID_IMAGE_URL=
SESSION_EXPIRY=(1000 * 60 * 60 * 24) * 7
# Github:
# Get the Client ID and Secret from your Github Application
# Get the Client ID and Secret from your Discord Application
# Add your Discord Client ID and Client Secret here:
GITHUB_CLIENT_ID=your_client_id
GITHUB_CLIENT_SECRET=your_client_secret
GITHUB_CALLBACK_URL=/oauth/github/callback # this should be the same for everyone
# Discord:
# Get the Client ID and Secret from your Discord Application
# Add your Github Client ID and Client Secret here:
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
GITHUB_CALLBACK_URL=/oauth/github/callback
DISCORD_CLIENT_ID=your_client_id
DISCORD_CLIENT_SECRET=your_client_secret
DISCORD_CALLBACK_URL=/oauth/discord/callback # this should be the same for everyone
###########################
# Application Domains

View file

@ -75,6 +75,11 @@ const userSchema = mongoose.Schema(
unique: true,
sparse: true
},
discordId: {
type: String,
unique: true,
sparse: true
},
plugins: {
type: Array,
default: []

View file

@ -45,6 +45,9 @@ config.validate(); // Validate the config
if (process.env.GITHUB_CLIENT_ID && process.env.GITHUB_CLIENT_SECRET) {
require('../strategies/githubStrategy');
}
if (process.env.DISCORD_CLIENT_ID && process.env.DISCORD_CLIENT_SECRET) {
require('../strategies/discordStrategy');
}
if (process.env.OPENID_CLIENT_ID && process.env.OPENID_CLIENT_SECRET &&
process.env.OPENID_ISSUER && process.env.OPENID_SCOPE &&
process.env.OPENID_SESSION_SECRET) {

View file

@ -16,8 +16,11 @@ afterEach(() => {
delete process.env.OPENID_AUTH_URL;
delete process.env.GITHUB_CLIENT_ID;
delete process.env.GITHUB_CLIENT_SECRET;
delete process.env.DISCORD_CLIENT_ID;
delete process.env.DISCORD_CLIENT_SECRET;
delete process.env.DOMAIN_SERVER;
delete process.env.ALLOW_REGISTRATION;
delete process.env.ALLOW_SOCIAL_LOGIN;
});
//TODO: This works/passes locally but http request tests fail with 404 in CI. Need to figure out why.
@ -36,8 +39,11 @@ describe.skip('GET /', () => {
process.env.OPENID_AUTH_URL= 'http://test-server.com';
process.env.GITHUB_CLIENT_ID = 'Test Github client Id';
process.env.GITHUB_CLIENT_SECRET= 'Test Github client Secret';
process.env.DISCORD_CLIENT_ID = 'Test Discord client Id';
process.env.DISCORD_CLIENT_SECRET= 'Test Discord client Secret';
process.env.DOMAIN_SERVER = 'http://test-server.com';
process.env.ALLOW_REGISTRATION = 'true';
process.env.ALLOW_SOCIAL_LOGIN = 'true';
const response = await request(app).get('/');
@ -49,8 +55,10 @@ describe.skip('GET /', () => {
openidLabel: 'Test OpenID',
openidImageUrl: 'http://test-server.com',
githubLoginEnabled: true,
discordLoginEnabled: true,
serverDomain: 'http://test-server.com',
registrationEnabled: 'true',
socialLoginEnabled: 'true',
});
});
});

View file

@ -12,10 +12,24 @@ router.get('/', async function (req, res) {
const openidLabel = process.env.OPENID_BUTTON_LABEL || 'Login with OpenID';
const openidImageUrl = process.env.OPENID_IMAGE_URL;
const githubLoginEnabled = !!process.env.GITHUB_CLIENT_ID && !!process.env.GITHUB_CLIENT_SECRET;
const discordLoginEnabled = !!process.env.DISCORD_CLIENT_ID && !!process.env.DISCORD_CLIENT_SECRET;
const serverDomain = process.env.DOMAIN_SERVER || 'http://localhost:3080';
const registrationEnabled = process.env.ALLOW_REGISTRATION === 'true';
const socialLoginEnabled = process.env.ALLOW_SOCIAL_LOGIN === 'true';
return res.status(200).send({
appTitle,
googleLoginEnabled,
openidLoginEnabled,
openidLabel,
openidImageUrl,
githubLoginEnabled,
discordLoginEnabled,
serverDomain,
registrationEnabled,
socialLoginEnabled
});
return res.status(200).send({appTitle, googleLoginEnabled, openidLoginEnabled, openidLabel, openidImageUrl, githubLoginEnabled, serverDomain, registrationEnabled});
} catch (err) {
console.error(err);
return res.status(500).send({error: err.message});

View file

@ -115,4 +115,32 @@ router.get(
}
);
router.get(
'/discord',
passport.authenticate('discord', {
scope: ['identify', 'email'],
session: false
})
);
router.get(
'/discord/callback',
passport.authenticate('discord', {
failureRedirect: `${domains.client}/login`,
failureMessage: true,
session: false,
scope: ['identify', 'email']
}),
(req, res) => {
const token = req.user.generateToken();
res.cookie('token', token, {
expires: new Date(Date.now() + eval(process.env.SESSION_EXPIRY)),
httpOnly: false,
secure: isProduction
});
res.redirect(domains.client);
}
);
module.exports = router;

View file

@ -0,0 +1,57 @@
const passport = require('passport');
const { Strategy: DiscordStrategy } = require('passport-discord');
const User = require('../models/User');
const config = require('../../config/loader');
const domains = config.domains;
const discordLogin = new DiscordStrategy(
{
clientID: process.env.DISCORD_CLIENT_ID,
clientSecret: process.env.DISCORD_CLIENT_SECRET,
callbackURL: `${domains.server}${process.env.DISCORD_CALLBACK_URL}`,
scope: ['identify', 'email'] // Request scopes
},
async (accessToken, refreshToken, profile, cb) => {
try {
const discordId = profile.id;
const email = profile.email;
const existingUser = await User.findOne({ discordId });
if (existingUser) {
return cb(null, existingUser);
}
const userWithEmail = await User.findOne({ email });
if (userWithEmail) {
userWithEmail.discordId = discordId;
await userWithEmail.save();
return cb(null, userWithEmail);
}
let avatarURL;
if (profile.avatar) {
const format = profile.avatar.startsWith('a_') ? 'gif' : 'png';
avatarURL = `https://cdn.discordapp.com/avatars/${profile.id}/${profile.avatar}.${format}`;
} else {
const defaultAvatarNum = Number(profile.discriminator) % 5;
avatarURL = `https://cdn.discordapp.com/embed/avatars/${defaultAvatarNum}.png`;
}
const newUser = await User.create({
provider: 'discord',
discordId,
username: profile.username,
email,
name: profile.global_name,
avatar: avatarURL
});
cb(null, newUser);
} catch (err) {
console.error(err);
cb(err);
}
}
);
passport.use(discordLogin);

View file

@ -1,4 +1,4 @@
import { useEffect } from 'react';
import React, { useEffect } from 'react';
import LoginForm from './LoginForm';
import { useAuthContext } from '~/hooks/AuthContext';
import { useNavigate } from 'react-router-dom';
@ -7,6 +7,7 @@ import { useRecoilValue } from 'recoil';
import store from '~/store';
import { localize } from '~/localization/Translation';
import { useGetStartupConfig } from '@librechat/data-provider';
import { GoogleIcon, OpenIDIcon, GithubIcon, DiscordIcon } from '~/components'
function Login() {
const { login, error, isAuthenticated } = useAuthContext();
@ -21,6 +22,7 @@ function Login() {
navigate('/chat/new');
}
}, [isAuthenticated, navigate]);
return (
<div className="flex min-h-screen flex-col items-center justify-center bg-white pt-6 sm:pt-0">
<div className="mt-6 w-96 overflow-hidden bg-white px-6 py-4 sm:max-w-md sm:rounded-lg">
@ -43,46 +45,26 @@ function Login() {
</a>
</p>
)}
{startupConfig?.googleLoginEnabled && (
<>
{startupConfig?.socialLoginEnabled && (
<div className="relative mt-6 flex w-full items-center justify-center border border-t uppercase">
<div className="absolute bg-white px-3 text-xs">Or</div>
</div>
)}
{startupConfig?.googleLoginEnabled && startupConfig?.socialLoginEnabled && (
<>
<div className="mt-4 flex gap-x-2">
<a
aria-label="Login with Google"
className="justify-left flex w-full items-center space-x-3 rounded-md border border-gray-300 px-5 py-3 hover:bg-gray-50 focus:ring-2 focus:ring-violet-600 focus:ring-offset-1"
href={`${startupConfig.serverDomain}/oauth/google`}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
id="google"
className="h-5 w-5"
>
<path
fill="#fbbb00"
d="M113.47 309.408 95.648 375.94l-65.139 1.378C11.042 341.211 0 299.9 0 256c0-42.451 10.324-82.483 28.624-117.732h.014L86.63 148.9l25.404 57.644c-5.317 15.501-8.215 32.141-8.215 49.456.002 18.792 3.406 36.797 9.651 53.408z"
></path>
<path
fill="#518ef8"
d="M507.527 208.176C510.467 223.662 512 239.655 512 256c0 18.328-1.927 36.206-5.598 53.451-12.462 58.683-45.025 109.925-90.134 146.187l-.014-.014-73.044-3.727-10.338-64.535c29.932-17.554 53.324-45.025 65.646-77.911h-136.89V208.176h245.899z"
></path>
<path
fill="#28b446"
d="m416.253 455.624.014.014C372.396 490.901 316.666 512 256 512c-97.491 0-182.252-54.491-225.491-134.681l82.961-67.91c21.619 57.698 77.278 98.771 142.53 98.771 28.047 0 54.323-7.582 76.87-20.818l83.383 68.262z"
></path>
<path
fill="#f14336"
d="m419.404 58.936-82.933 67.896C313.136 112.246 285.552 103.82 256 103.82c-66.729 0-123.429 42.957-143.965 102.724l-83.397-68.276h-.014C71.23 56.123 157.06 0 256 0c62.115 0 119.068 22.126 163.404 58.936z"
></path>
</svg>
href={`${startupConfig.serverDomain}/oauth/google`}>
<GoogleIcon />
<p>{localize(lang, 'com_auth_google_login')}</p>
</a>
</div>
</>
)}
{startupConfig?.openidLoginEnabled && (
{startupConfig?.openidLoginEnabled && startupConfig?.socialLoginEnabled && (
<>
<div className="mt-4 flex gap-x-2">
<a
@ -93,52 +75,44 @@ function Login() {
{startupConfig.openidImageUrl ? (
<img src={startupConfig.openidImageUrl} alt="OpenID Logo" className="h-5 w-5" />
) : (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 448 512"
id="openid"
className="h-5 w-5"
>
<path d="M271.5 432l-68 32C88.5 453.7 0 392.5 0 318.2c0-71.5 82.5-131 191.7-144.3v43c-71.5 12.5-124 53-124 101.3 0 51 58.5 93.3 135.7 103v-340l68-33.2v384zM448 291l-131.3-28.5 36.8-20.7c-19.5-11.5-43.5-20-70-24.8v-43c46.2 5.5 87.7 19.5 120.3 39.3l35-19.8L448 291z"></path>
</svg>
<OpenIDIcon />
)}
<p>{startupConfig.openidLabel}</p>
</a>
</div>
</>
)}
{startupConfig?.githubLoginEnabled && (
{startupConfig?.githubLoginEnabled && startupConfig?.socialLoginEnabled && (
<>
<div className="relative mt-6 flex w-full items-center justify-center border border-t uppercase">
<div className="absolute bg-white px-3 text-xs">Or</div>
</div>
<div className="mt-4 flex gap-x-2">
<a
aria-label="Login with GitHub"
className="justify-left flex w-full items-center space-x-3 rounded-md border border-gray-300 px-5 py-3 hover:bg-gray-50 focus:ring-2 focus:ring-violet-600 focus:ring-offset-1"
href={`${startupConfig.serverDomain}/oauth/github`}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
id="github"
className="h-5 w-5"
>
<path
fill="currentColor"
d="M12 0a12 12 0 0 0-3.84 23.399c.608.112.832-.256.832-.576v-2.015c-3.395.736-4.115-1.632-4.115-1.632a3.241 3.241 0 0 0-1.359-1.792c-1.104-.736.064-.736.064-.736a2.566 2.566 0 0 1 1.824 1.216a2.638 2.638 0 0 0 3.616 1.024a2.607 2.607 0 0 1 .768-1.6c-2.688-.32-5.504-1.344-5.504-5.984a4.677 4.677 0 0 1 1.216-3.168a4.383 4.383 0 0 1 .128-3.136s1.024-.32 3.36 1.216a11.66 11.66 0 0 1 6.112 0c2.336-1.536 3.36-1.216 3.36-1.216a4.354 4.354 0 0 1 .128 3.136a4.628 4.628 0 0 1 1.216 3.168c0 4.672-2.848 5.664-5.536 5.952a2.881 2.881 0 0 1 .832 2.24v3.36c0 .32.224.672.832.576A12 12 0 0 0 12 0z"
/>
</svg>
<p>Login with GitHub</p>
href={`${startupConfig.serverDomain}/oauth/github`}>
<GithubIcon />
<p>Login with Github</p>
</a>
</div>
</>
)}
{startupConfig?.discordLoginEnabled && startupConfig?.socialLoginEnabled && (
<>
<div className="mt-4 flex gap-x-2">
<a
aria-label="Login with Discord"
className="justify-left flex w-full items-center space-x-3 rounded-md border border-gray-300 px-5 py-3 hover:bg-gray-50 focus:ring-2 focus:ring-violet-600 focus:ring-offset-1"
href={`${startupConfig.serverDomain}/oauth/discord`}>
<DiscordIcon />
<p>Login with Discord</p>
</a>
</div>
</>
)}
</div>
</div>
);
}
};
export default Login;

View file

@ -9,6 +9,7 @@ import {
TRegisterUser,
useGetStartupConfig
} from '@librechat/data-provider';
import { GoogleIcon, OpenIDIcon, GithubIcon, DiscordIcon } from '~/components'
function Registration() {
const navigate = useNavigate();
@ -283,51 +284,28 @@ function Registration() {
{localize(lang, 'com_auth_login')}
</a>
</p>
{startupConfig?.googleLoginEnabled && (
<>
{startupConfig?.socialLoginEnabled && (
<div className="relative mt-6 flex w-full items-center justify-center border border-t uppercase">
<div className="absolute bg-white px-3 text-xs">Or</div>
</div>
)}
{startupConfig?.googleLoginEnabled && startupConfig?.socialLoginEnabled && (
<>
<div className="mt-4 flex gap-x-2">
<a
aria-label="Login with Google"
href={`${startupConfig.serverDomain}/oauth/google`}
className="justify-left flex w-full items-center space-x-3 rounded-md border border-gray-300 px-5 py-3 hover:bg-gray-50 focus:ring-2 focus:ring-violet-600 focus:ring-offset-1"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
id="google"
className="h-5 w-5"
>
<path
fill="#fbbb00"
d="M113.47 309.408 95.648 375.94l-65.139 1.378C11.042 341.211 0 299.9 0 256c0-42.451 10.324-82.483 28.624-117.732h.014L86.63 148.9l25.404 57.644c-5.317 15.501-8.215 32.141-8.215 49.456.002 18.792 3.406 36.797 9.651 53.408z"
></path>
<path
fill="#518ef8"
d="M507.527 208.176C510.467 223.662 512 239.655 512 256c0 18.328-1.927 36.206-5.598 53.451-12.462 58.683-45.025 109.925-90.134 146.187l-.014-.014-73.044-3.727-10.338-64.535c29.932-17.554 53.324-45.025 65.646-77.911h-136.89V208.176h245.899z"
></path>
<path
fill="#28b446"
d="m416.253 455.624.014.014C372.396 490.901 316.666 512 256 512c-97.491 0-182.252-54.491-225.491-134.681l82.961-67.91c21.619 57.698 77.278 98.771 142.53 98.771 28.047 0 54.323-7.582 76.87-20.818l83.383 68.262z"
></path>
<path
fill="#f14336"
d="m419.404 58.936-82.933 67.896C313.136 112.246 285.552 103.82 256 103.82c-66.729 0-123.429 42.957-143.965 102.724l-83.397-68.276h-.014C71.23 56.123 157.06 0 256 0c62.115 0 119.068 22.126 163.404 58.936z"
></path>
</svg>
href={`${startupConfig.serverDomain}/oauth/google`}>
<GoogleIcon />
<p>{localize(lang, 'com_auth_google_login')}</p>
</a>
</div>
</>
)}
{startupConfig?.openidLoginEnabled && (
{startupConfig?.openidLoginEnabled && startupConfig?.socialLoginEnabled && (
<>
<div className="relative mt-6 flex w-full items-center justify-center border border-t uppercase">
<div className="absolute bg-white px-3 text-xs">Or</div>
</div>
<div className="mt-4 flex gap-x-2">
<a
aria-label="Login with OpenID"
@ -337,48 +315,42 @@ function Registration() {
{startupConfig.openidImageUrl ? (
<img src={startupConfig.openidImageUrl} alt="OpenID Logo" className="h-5 w-5" />
) : (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 448 512"
id="openid"
className="h-5 w-5"
>
<path d="M271.5 432l-68 32C88.5 453.7 0 392.5 0 318.2c0-71.5 82.5-131 191.7-144.3v43c-71.5 12.5-124 53-124 101.3 0 51 58.5 93.3 135.7 103v-340l68-33.2v384zM448 291l-131.3-28.5 36.8-20.7c-19.5-11.5-43.5-20-70-24.8v-43c46.2 5.5 87.7 19.5 120.3 39.3l35-19.8L448 291z"></path>
</svg>
<OpenIDIcon />
)}
<p>{startupConfig.openidLabel}</p>
</a>
</div>
</>
)}
{startupConfig?.githubLoginEnabled && (
{startupConfig?.githubLoginEnabled && startupConfig?.socialLoginEnabled && (
<>
<div className="relative mt-6 flex w-full items-center justify-center border border-t uppercase">
<div className="absolute bg-white px-3 text-xs">Or</div>
</div>
<div className="mt-4 flex gap-x-2">
<a
aria-label="Login with GitHub"
href={`${startupConfig.serverDomain}/oauth/github`}
className="justify-left flex w-full items-center space-x-3 rounded-md border border-gray-300 px-5 py-3 hover:bg-gray-50 focus:ring-2 focus:ring-violet-600 focus:ring-offset-1"
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
id="github"
className="h-5 w-5"
>
<path
fill="currentColor"
d="M12 0a12 12 0 0 0-3.84 23.399c.608.112.832-.256.832-.576v-2.015c-3.395.736-4.115-1.632-4.115-1.632a3.241 3.241 0 0 0-1.359-1.792c-1.104-.736.064-.736.064-.736a2.566 2.566 0 0 1 1.824 1.216a2.638 2.638 0 0 0 3.616 1.024a2.607 2.607 0 0 1 .768-1.6c-2.688-.32-5.504-1.344-5.504-5.984a4.677 4.677 0 0 1 1.216-3.168a4.383 4.383 0 0 1 .128-3.136s1.024-.32 3.36 1.216a11.66 11.66 0 0 1 6.112 0c2.336-1.536 3.36-1.216 3.36-1.216a4.354 4.354 0 0 1 .128 3.136a4.628 4.628 0 0 1 1.216 3.168c0 4.672-2.848 5.664-5.536 5.952a2.881 2.881 0 0 1 .832 2.24v3.36c0 .32.224.672.832.576A12 12 0 0 0 12 0z"
/>
</svg>
<p>Login with GitHub</p>
href={`${startupConfig.serverDomain}/oauth/github`}>
<GithubIcon />
<p>Login with Github</p>
</a>
</div>
</>
)}
{startupConfig?.discordLoginEnabled && startupConfig?.socialLoginEnabled && (
<>
<div className="mt-4 flex gap-x-2">
<a
aria-label="Login with Discord"
className="justify-left flex w-full items-center space-x-3 rounded-md border border-gray-300 px-5 py-3 hover:bg-gray-50 focus:ring-2 focus:ring-violet-600 focus:ring-offset-1"
href={`${startupConfig.serverDomain}/oauth/discord`}>
<DiscordIcon />
<p>Login with Discord</p>
</a>
</div>
</>
)}
</div>
</div>
);

View file

@ -27,7 +27,9 @@ const setup = ({
openidLabel: 'Test OpenID',
openidImageUrl: 'http://test-server.com',
githubLoginEnabled: true,
discordLoginEnabled: true,
registrationEnabled: true,
socialLoginEnabled: true,
serverDomain: 'mock-server'
}
}

View file

@ -27,7 +27,9 @@ const setup = ({
openidLabel: 'Test OpenID',
openidImageUrl: 'http://test-server.com',
githubLoginEnabled: true,
discordLoginEnabled: true,
registrationEnabled: true,
socialLoginEnabled: true,
serverDomain: 'mock-server'
}
}

View file

@ -0,0 +1,12 @@
import React from 'react';
export default function DiscordIcon() {
return (
<svg
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" id="discord" className="h-6 w-6"
>
<circle cx="512" cy="512" r="512" fill="#5865f2"/>
<path fill="#fff" d="M689.43 349a422.21 422.21 0 0 0-104.22-32.32 1.58 1.58 0 0 0-1.68.79 294.11 294.11 0 0 0-13 26.66 389.78 389.78 0 0 0-117.05 0 269.75 269.75 0 0 0-13.18-26.66 1.64 1.64 0 0 0-1.68-.79A421 421 0 0 0 334.44 349a1.49 1.49 0 0 0-.69.59c-66.37 99.17-84.55 195.9-75.63 291.41a1.76 1.76 0 0 0 .67 1.2 424.58 424.58 0 0 0 127.85 64.63 1.66 1.66 0 0 0 1.8-.59 303.45 303.45 0 0 0 26.15-42.54 1.62 1.62 0 0 0-.89-2.25 279.6 279.6 0 0 1-39.94-19 1.64 1.64 0 0 1-.16-2.72c2.68-2 5.37-4.1 7.93-6.22a1.58 1.58 0 0 1 1.65-.22c83.79 38.26 174.51 38.26 257.31 0a1.58 1.58 0 0 1 1.68.2c2.56 2.11 5.25 4.23 8 6.24a1.64 1.64 0 0 1-.14 2.72 262.37 262.37 0 0 1-40 19 1.63 1.63 0 0 0-.87 2.28 340.72 340.72 0 0 0 26.13 42.52 1.62 1.62 0 0 0 1.8.61 423.17 423.17 0 0 0 128-64.63 1.64 1.64 0 0 0 .67-1.18c10.68-110.44-17.88-206.38-75.7-291.42a1.3 1.3 0 0 0-.63-.63zM427.09 582.85c-25.23 0-46-23.16-46-51.6s20.38-51.6 46-51.6c25.83 0 46.42 23.36 46 51.6.02 28.44-20.37 51.6-46 51.6zm170.13 0c-25.23 0-46-23.16-46-51.6s20.38-51.6 46-51.6c25.83 0 46.42 23.36 46 51.6.01 28.44-20.17 51.6-46 51.6z"></path>
</svg>
);
}

View file

@ -0,0 +1,12 @@
import React from 'react';
export default function GithubIcon() {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="25" height="25" fill="none">
<path
fill="currentColor"
d="M12 0a12 12 0 0 0-3.84 23.399c.608.112.832-.256.832-.576v-2.015c-3.395.736-4.115-1.632-4.115-1.632a3.241 3.241 0 0 0-1.359-1.792c-1.104-.736.064-.736.064-.736a2.566 2.566 0 0 1 1.824 1.216a2.638 2.638 0 0 0 3.616 1.024a2.607 2.607 0 0 1 .768-1.6c-2.688-.32-5.504-1.344-5.504-5.984a4.677 4.677 0 0 1 1.216-3.168a4.383 4.383 0 0 1 .128-3.136s1.024-.32 3.36 1.216a11.66 11.66 0 0 1 6.112 0c2.336-1.536 3.36-1.216 3.36-1.216a4.354 4.354 0 0 1 .128 3.136a4.628 4.628 0 0 1 1.216 3.168c0 4.672-2.848 5.664-5.536 5.952a2.881 2.881 0 0 1 .832 2.24v3.36c0 .32.224.672.832.576A12 12 0 0 0 12 0z"
/>
</svg>
);
}

View file

@ -0,0 +1,29 @@
import React from 'react';
export default function GoogleIcon() {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
id="google"
className="h-5 w-5"
>
<path
fill="#fbbb00"
d="M113.47 309.408 95.648 375.94l-65.139 1.378C11.042 341.211 0 299.9 0 256c0-42.451 10.324-82.483 28.624-117.732h.014L86.63 148.9l25.404 57.644c-5.317 15.501-8.215 32.141-8.215 49.456.002 18.792 3.406 36.797 9.651 53.408z"
></path>
<path
fill="#518ef8"
d="M507.527 208.176C510.467 223.662 512 239.655 512 256c0 18.328-1.927 36.206-5.598 53.451-12.462 58.683-45.025 109.925-90.134 146.187l-.014-.014-73.044-3.727-10.338-64.535c29.932-17.554 53.324-45.025 65.646-77.911h-136.89V208.176h245.899z"
></path>
<path
fill="#28b446"
d="m416.253 455.624.014.014C372.396 490.901 316.666 512 256 512c-97.491 0-182.252-54.491-225.491-134.681l82.961-67.91c21.619 57.698 77.278 98.771 142.53 98.771 28.047 0 54.323-7.582 76.87-20.818l83.383 68.262z"
></path>
<path
fill="#f14336"
d="m419.404 58.936-82.933 67.896C313.136 112.246 285.552 103.82 256 103.82c-66.729 0-123.429 42.957-143.965 102.724l-83.397-68.276h-.014C71.23 56.123 157.06 0 256 0c62.115 0 119.068 22.126 163.404 58.936z"
></path>
</svg>
);
}

View file

@ -0,0 +1,11 @@
import React from 'react';
export default function OpenIDIcon() {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" id="openid" className="h-5 w-5">
<path
fill="currentColor"
d="M271.5 432l-68 32C88.5 453.7 0 392.5 0 318.2c0-71.5 82.5-131 191.7-144.3v43c-71.5 12.5-124 53-124 101.3 0 51 58.5 93.3 135.7 103v-340l68-33.2v384zM448 291l-131.3-28.5 36.8-20.7c-19.5-11.5-43.5-20-70-24.8v-43c46.2 5.5 87.7 19.5 120.3 39.3l35-19.8L448 291z"></path>
</svg>
);
}

View file

@ -5,3 +5,7 @@ export { default as CogIcon } from './CogIcon';
export { default as Spinner } from './Spinner';
export { default as MessagesSquared } from './MessagesSquared';
export { default as StopGeneratingIcon } from './StopGeneratingIcon';
export { default as GoogleIcon } from './GoogleIcon';
export { default as OpenIDIcon } from './OpenIDIcon';
export { default as GithubIcon } from './GithubIcon';
export { default as DiscordIcon } from './DiscordIcon';

View file

@ -77,9 +77,29 @@ OPENID_CALLBACK_URL=/oauth/openid/callback
5. Remove the Active checkbox in the Webhook section
6. Save changes and generate a Client Secret
7. In the Permissions & events tab select, open the Account Permissions and set Email addresses to Read-only
8. Put the Client ID and Client Secret in the .env file
8. Put the Client ID and Client Secret in the .env file:
```
GITHUB_CLIENT_ID=your_client_id
GITHUB_CLIENT_SECRET=your_client_secret
GITHUB_CALLBACK_URL=/oauth/github/callback # this should be the same for everyone
```
9. Save the .env file
---
## How to Set Up Discord Authentication
1. Go to [Discord Developer Portal](https://discord.com/developers)
2. Create a new Application and give it a name
4. In the OAuth2 general settings add a redirect URL and set it as "[Your DOMAIN_CLIENT](https://github.com/danny-avila/LibreChat/blob/main/.env.example#L219)/oauth/discord/callback" (example: http://localhost:3080/oauth/discord/callback)
5. in the Default Authorization Link set applications.commands
6. Save changes and reset the Client Secret
7. Put the Client ID and Client Secret in the .env file:
```
DISCORD_CLIENT_ID=your_client_id
DISCORD_CLIENT_SECRET=your_client_secret
DISCORD_CALLBACK_URL=/oauth/discord/callback # this should be the same for everyone
```
8. Save the .env file
---
## **Email and Password Reset**

17
package-lock.json generated
View file

@ -17,6 +17,7 @@
"dependencies": {
"axios": "^1.4.0",
"passport": "^0.6.0",
"passport-discord": "^0.1.4",
"passport-github2": "^0.1.12"
},
"devDependencies": {
@ -20404,6 +20405,14 @@
"url": "https://github.com/sponsors/jaredhanson"
}
},
"node_modules/passport-discord": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/passport-discord/-/passport-discord-0.1.4.tgz",
"integrity": "sha512-VJWPYqSOmh7SaCLw/C+k1ZqCzJnn2frrmQRx1YrcPJ3MQ+Oa31XclbbmqFICSvl8xv3Fqd6YWQ4H4p1MpIN9rA==",
"dependencies": {
"passport-oauth2": "^1.5.0"
}
},
"node_modules/passport-facebook": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/passport-facebook/-/passport-facebook-3.0.0.tgz",
@ -41105,6 +41114,14 @@
"utils-merge": "^1.0.1"
}
},
"passport-discord": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/passport-discord/-/passport-discord-0.1.4.tgz",
"integrity": "sha512-VJWPYqSOmh7SaCLw/C+k1ZqCzJnn2frrmQRx1YrcPJ3MQ+Oa31XclbbmqFICSvl8xv3Fqd6YWQ4H4p1MpIN9rA==",
"requires": {
"passport-oauth2": "^1.5.0"
}
},
"passport-facebook": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/passport-facebook/-/passport-facebook-3.0.0.tgz",

View file

@ -41,6 +41,7 @@
"dependencies": {
"axios": "^1.4.0",
"passport": "^0.6.0",
"passport-discord": "^0.1.4",
"passport-github2": "^0.1.12"
},
"devDependencies": {

View file

@ -252,8 +252,10 @@ export type TStartupConfig = {
githubLoginEnabled: boolean;
openidLabel: string;
openidImageUrl: string;
discordLoginEnabled: boolean;
serverDomain: string;
registrationEnabled: boolean;
socialLoginEnabled: boolean;
}
export type TRefreshTokenResponse = {