Skip to content

Commit

Permalink
GIthub Login (danny-avila#578)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
berry-13 authored Jul 4, 2023
1 parent 8819e83 commit d0078d4
Show file tree
Hide file tree
Showing 15 changed files with 199 additions and 4 deletions.
8 changes: 8 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,14 @@ OPENID_IMAGE_URL=
# Delay is in millisecond e.g. 7 days is 1000*60*60*24*7
SESSION_EXPIRY=(1000 * 60 * 60 * 24) * 7

# Github:
# Get the Client ID and Secret from your Github Application
# Add your Github Client ID and Client Secret here:

GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
GITHUB_CALLBACK_URL=/oauth/github/callback

###########################
# Application Domains
###########################
Expand Down
5 changes: 5 additions & 0 deletions api/models/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ const userSchema = mongoose.Schema(
unique: true,
sparse: true
},
githubId: {
type: String,
unique: true,
sparse: true
},
plugins: {
type: Array,
default: []
Expand Down
3 changes: 3 additions & 0 deletions api/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ config.validate(); // Validate the config
if (process.env.FACEBOOK_CLIENT_ID && process.env.FACEBOOK_CLIENT_SECRET) {
require('../strategies/facebookStrategy');
}
if (process.env.GITHUB_CLIENT_ID && process.env.GITHUB_CLIENT_SECRET) {
require('../strategies/githubStrategy');
}
if (process.env.OPENID_CLIENT_ID && process.env.OPENID_CLIENT_SECRET &&
process.env.OPENID_ISSUER && process.env.OPENID_SCOPE &&
process.env.OPENID_SESSION_SECRET) {
Expand Down
5 changes: 5 additions & 0 deletions api/server/routes/__tests__/config.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ afterEach(() => {
delete process.env.OPENID_SESSION_SECRET;
delete process.env.OPENID_BUTTON_LABEL;
delete process.env.OPENID_AUTH_URL;
delete process.env.GITHUB_CLIENT_ID;
delete process.env.GITHUB_CLIENT_SECRET;
delete process.env.DOMAIN_SERVER;
delete process.env.ALLOW_REGISTRATION;
});
Expand All @@ -32,6 +34,8 @@ describe.skip('GET /', () => {
process.env.OPENID_SESSION_SECRET= 'Test Secret';
process.env.OPENID_BUTTON_LABEL= 'Test OpenID';
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.DOMAIN_SERVER = 'http://test-server.com';
process.env.ALLOW_REGISTRATION = 'true';

Expand All @@ -44,6 +48,7 @@ describe.skip('GET /', () => {
openidLoginEnabled: true,
openidLabel: 'Test OpenID',
openidImageUrl: 'http://test-server.com',
githubLoginEnabled: true,
serverDomain: 'http://test-server.com',
registrationEnabled: 'true',
});
Expand Down
3 changes: 2 additions & 1 deletion api/server/routes/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ router.get('/', async function (req, res) {
&& !!process.env.OPENID_SESSION_SECRET;
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 serverDomain = process.env.DOMAIN_SERVER || 'http://localhost:3080';
const registrationEnabled = process.env.ALLOW_REGISTRATION === 'true';

return res.status(200).send({appTitle, googleLoginEnabled, openidLoginEnabled, openidLabel, openidImageUrl, serverDomain, registrationEnabled});
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});
Expand Down
28 changes: 28 additions & 0 deletions api/server/routes/oauth.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,32 @@ router.get(
}
);


router.get(
'/github',
passport.authenticate('github', {
scope: ['user:email', 'read:user'],
session: false
})
);

router.get(
'/github/callback',
passport.authenticate('github', {
failureRedirect: `${domains.client}/login`,
failureMessage: true,
session: false,
scope: ['user:email', 'read:user']
}),
(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;
47 changes: 47 additions & 0 deletions api/strategies/githubStrategy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const passport = require('passport');
const { Strategy: GitHubStrategy } = require('passport-github2');
const config = require('../../config/loader');
const domains = config.domains;

const User = require('../models/User');

// GitHub strategy
const githubLogin = new GitHubStrategy(
{
clientID: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
callbackURL: `${domains.server}${process.env.GITHUB_CALLBACK_URL}`,
proxy: false,
scope: ['user:email'] // Request email scope
},
async (accessToken, refreshToken, profile, cb) => {
try {
let email;
if (profile.emails && profile.emails.length > 0) {
email = profile.emails[0].value;
}

const oldUser = await User.findOne({ email });
if (oldUser) {
return cb(null, oldUser);
}

const newUser = await new User({
provider: 'github',
githubId: profile.id,
username: profile.username,
email,
emailVerified: profile.emails[0].verified,
name: profile.displayName,
avatar: profile.photos[0].value
}).save();

cb(null, newUser);
} catch (err) {
console.error(err);
cb(err);
}
}
);

passport.use(githubLogin);
29 changes: 29 additions & 0 deletions client/src/components/Auth/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,38 @@ function Login() {
</div>
</>
)}
{startupConfig?.githubLoginEnabled && (
<>
<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>
</a>
</div>
</>
)}

</div>
</div>
);
}


export default Login;
28 changes: 28 additions & 0 deletions client/src/components/Auth/Registration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,34 @@ function Registration() {
</div>
</>
)}
{startupConfig?.githubLoginEnabled && (
<>
<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>
</a>
</div>
</>
)}

</div>
</div>
);
Expand Down
1 change: 1 addition & 0 deletions client/src/components/Auth/__tests__/Login.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const setup = ({
openidLoginEnabled: true,
openidLabel: 'Test OpenID',
openidImageUrl: 'http://test-server.com',
githubLoginEnabled: true,
registrationEnabled: true,
serverDomain: 'mock-server'
}
Expand Down
1 change: 1 addition & 0 deletions client/src/components/Auth/__tests__/Registration.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const setup = ({
openidLoginEnabled: true,
openidLabel: 'Test OpenID',
openidImageUrl: 'http://test-server.com',
githubLoginEnabled: true,
registrationEnabled: true,
serverDomain: 'mock-server'
}
Expand Down
1 change: 1 addition & 0 deletions client/src/data-provider/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ export type TStartupConfig = {
openidLoginEnabled: boolean;
openidLabel: string;
openidImageUrl: string;
githubLoginEnabled: boolean;
serverDomain: string;
registrationEnabled: boolean;
}
15 changes: 14 additions & 1 deletion docs/features/user_auth_system.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,19 @@ OPENID_CALLBACK_URL=/oauth/openid/callback

---

## How to Set Up Github Authentication

1. Go to your [Github Developer settings](https://github.com/settings/apps)
2. Create a new Github app
3. Give it a GitHub App name and set in the Homepage URL your [DOMAIN_CLIENT](https://github.com/danny-avila/LibreChat/blob/main/.env.example#L219) (example: http://localhost:3080)
4. Add a callback URL and set it as "[Your DOMAIN_CLIENT](https://github.com/danny-avila/LibreChat/blob/main/.env.example#L219)/oauth/github/callback" (example: http://localhost:3080/oauth/github/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
9. Save the .env file

---
## **Email and Password Reset**

Most of the code is in place for sending password reset emails, but is not yet feature-complete as I have not setup an email server to test it. Currently, submitting a password reset request will then display a link with the one-time reset token that can then be used to reset the password. Understanding that this is a considerable security hazard, email integration will be included in the next release.
Expand All @@ -83,4 +96,4 @@ If you previously implemented your own user system using the original scaffoldin
### For user updating from an older version of the app:

When the first account is registered, the application will automatically migrate any conversations and presets that you created before the user system was implemented to that account.
if you use login for the first time with a social login account (eg. Google, facebook, etc.), the conversations and presets that you created before the user system was implemented will NOT be migrated to that account.
if you use login for the first time with a social login account (eg. Google, facebook, etc.), the conversations and presets that you created before the user system was implemented will NOT be migrated to that account.
24 changes: 23 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@
},
"homepage": "https://github.com/danny-avila/LibreChat#readme",
"dependencies": {
"langchain": "^0.0.91"
"axios": "^1.4.0",
"langchain": "^0.0.91",
"passport": "^0.6.0",
"passport-github2": "^0.1.12"
},
"devDependencies": {
"@playwright/test": "^1.32.1",
Expand Down

0 comments on commit d0078d4

Please sign in to comment.