mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-09-21 21:50:49 +02:00
OpenID Authentication (#495)
* Squashed commit of the following:
commit 26ab03fb36fcc7fcee63fdf3ae8c2dfb29027eff
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Tue Jun 13 00:23:23 2023 -0500
Update Registration.spec.tsx
commit e908dd82fe9ef1b43c75ee64c183d2f654bdac1c
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Tue Jun 13 00:23:01 2023 -0500
Update Login.spec.tsx
commit 223734820fb77d7fb5af4802af642d1c1fd7c1f5
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Tue Jun 13 00:22:39 2023 -0500
Update Registration.tsx
commit 7036d3dd0538979ee397d958ebc113bb0ea32411
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Tue Jun 13 00:21:55 2023 -0500
Update Login.tsx
commit 76bb78221db3195fd930fe9cfd6a5da7194fa759
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Tue Jun 13 00:21:03 2023 -0500
Update envConstants.js
commit ee2f69f33d75fbb57022afbcd9564bca38a46bee
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Tue Jun 13 00:20:08 2023 -0500
Update docker-compose.yml
commit 5ac72d789b3446884c6e2f4f595cbf67d731d43c
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Tue Jun 13 00:18:41 2023 -0500
Update Dockerfile
commit d24341db2bd5b17eb89ab01e171a5f51f3beab0a
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Tue Jun 13 00:16:38 2023 -0500
Update .env.example
commit 22154f4a09c5fcdfee95d43609fb01a5a883b7a9
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Tue Jun 13 00:07:48 2023 -0500
Update Registration.spec.tsx
commit 5163f7d372a6a03c94f4357b358211a03369456e
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Tue Jun 13 00:07:30 2023 -0500
Update Login.spec.tsx
commit 61da49e330a9376e130b24dc944854f97ab58d80
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Tue Jun 13 00:07:00 2023 -0500
Update Registration.tsx
commit 0e45d3f0dbde34388ff2f0b2dc51b983b472eb05
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Tue Jun 13 00:06:18 2023 -0500
Update Login.tsx
commit dca1e5367e5f3b468c7964218cc5914ca53095af
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Tue Jun 13 00:05:07 2023 -0500
Update envConstants.js
commit f48c058465d82b03716ba85224e9f97007e014d2
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Tue Jun 13 00:04:05 2023 -0500
Update .env.example
commit 818226c9cb079acae4fcbfe5997e4aa9e3c6d2cc
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Mon Jun 12 23:59:08 2023 -0500
Update .env.example
commit 9a805439189b352a38ac7654d7a31bb28f0f58dd
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Mon Jun 12 23:58:31 2023 -0500
Update env.d.ts
commit 3f37ce54758b017c9281b7fad9b040a47630ec66
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Mon Jun 12 23:57:04 2023 -0500
Update .env.example
commit 1026036f4dd529e9531c53084450ce768cfca4c1
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Mon Jun 12 23:50:36 2023 -0500
Update docker-compose.yml
commit a61cf7b8c51d4a9bd73a20bd67abc29891c11463
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Mon Jun 12 23:50:00 2023 -0500
Update Dockerfile
commit 79610d6648755cd5ec45215b9fdbe04ba8242fcf
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Mon Jun 12 23:35:34 2023 -0500
Update package-lock.json
commit e40853fd2b77f2db5be1c3dfd8b170d650e23271
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Mon Jun 12 23:30:17 2023 -0500
Update envConstants.js
commit 5529bc61b43f279fb4418c3851be2f9011b6454d
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Mon Jun 12 23:25:58 2023 -0500
Update docker-compose.yml
commit 07848cc464a64f7cad484e24a1310dc61aa03b18
Merge: ec628a3 72e9828
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Mon Jun 12 23:24:03 2023 -0500
Merge branch 'danny-avila:main' into openid-client
commit ec628a3044ba963b4e733c72229400074e7c2bc4
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Mon Jun 12 23:23:16 2023 -0500
Update envConstants.js
commit 21272221db0f58c244f08335482d45b177d338ab
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Mon Jun 12 23:21:59 2023 -0500
Update Registration.spec.tsx
commit d3f2949c0484d5760e7b689501852f86209992a3
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Mon Jun 12 23:21:12 2023 -0500
Update Login.spec.tsx
commit f2cf23ddd6708a3bb8d032dde5f1ce300dbe8cad
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Mon Jun 12 23:20:15 2023 -0500
Update Registration.tsx
commit 482c346b2a7baf958665c9474223d2557504dee5
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Mon Jun 12 23:17:53 2023 -0500
Update Login.tsx
commit 2f017aa5bf4ef91b73fe027fb346132e1a5d8b87
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Mon Jun 12 23:14:17 2023 -0500
Update env.d.ts
commit addfd95cf93ef19cae05bab652d634af64313e6a
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Mon Jun 12 23:13:16 2023 -0500
Create openidStrategy.js
commit 84c3b5c2f078494d8380f3a02e3ba2d935d8d79f
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Mon Jun 12 23:09:02 2023 -0500
Update oauth.js
commit 63225cdf33b7f42005b4a446797acbd91b7ee4a7
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Mon Jun 12 23:07:35 2023 -0500
Update index.js
commit 6efe4dafd4359ed1c3139468bf9d43f70bbaf6aa
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Mon Jun 12 23:04:55 2023 -0500
Update package.json
commit 201badbbb5a5c8d48f5c4cba3a1349d4cfc7a070
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Mon Jun 12 23:03:37 2023 -0500
Update User.js
commit 7d13d5c303465be9b1268e5f6d9bdf7bb8dfb2e4
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Mon Jun 12 23:02:29 2023 -0500
Update Dockerfile
commit 2ef7f84ea77f281c3dce61211d9fd841a6424e65
Author: bsu3338 <bsu3338@users.noreply.github.com>
Date: Mon Jun 12 23:00:42 2023 -0500
Update .env.example
* Update openidStrategy.js
* Update .env.example
* Update .env.example
* Update docker-compose.yml
* Update env.d.ts
* Update .env.example
* Update .env.example
* Update config.js
* Update Login.tsx
* Update config.js
* Update Login.tsx
* Update Registration.tsx
* Update docker-compose.yml
* Update openidStrategy.js
* Update docker-compose.yml
* Update config.spec.js
* Update Login.spec.tsx
* Update Registration.spec.tsx
* Update types.ts
* Update .env.example
* Update package-lock.json
* Update openidStrategy.js
* Update openidStrategy.js
* Update config.js
* Update config.js
* Update Login.tsx
* Update Registration.tsx
* Update oauth.js
* Update openidStrategy.js
* Update openidStrategy.js
* Update Registration.tsx
* Update Login.tsx
* Update Login.tsx
* Update Registration.tsx
* Update Registration.tsx
* Update index.js
* Update index.js
* Update .env.example
* Update user_auth_system.md
updated instruction that includes OpenID set up
* Update package.json
* Update package-lock.json
* Update package-lock.json
* Update package-lock.json
* Update package-lock.json
* Update package-lock.json
* Update package-lock.json
* Update package-lock.json
* Update package-lock.json
* Update openidStrategy.js
* Update openidStrategy.js
Lookup user based on openID instead of email. This is because not all AzureAD users may have an email tied to their account
* Update openidStrategy.js
First try to match an email, then try openIdID
* Update openidStrategy.js
* Update openidStrategy.js
Consider a family name or given name is not provided
---------
Co-authored-by: Fuegovic <32828263+fuegovic@users.noreply.github.com>
This commit is contained in:
parent
7efb90366f
commit
eceba36f54
17 changed files with 44617 additions and 44101 deletions
16
.env.example
16
.env.example
|
@ -187,6 +187,22 @@ GOOGLE_CLIENT_ID=
|
|||
GOOGLE_CLIENT_SECRET=
|
||||
GOOGLE_CALLBACK_URL=/oauth/google/callback
|
||||
|
||||
# OpenID:
|
||||
# See OpenID provider to get the below values
|
||||
# Create random string for OPENID_SESSION_SECRET
|
||||
# For Azure AD
|
||||
# ISSUER: https://login.microsoftonline.com/(tenant id)/v2.0/
|
||||
# SCOPE: openid profile email
|
||||
OPENID_CLIENT_ID=
|
||||
OPENID_CLIENT_SECRET=
|
||||
OPENID_ISSUER=
|
||||
OPENID_SESSION_SECRET=
|
||||
OPENID_SCOPE="openid profile email"
|
||||
OPENID_CALLBACK_URL=/oauth/openid/callback
|
||||
# If LABEL and URL are left empty, then the default OpenID label and logo are used.
|
||||
VITE_OPENID_LABEL=
|
||||
VITE_OPENID_URL=
|
||||
|
||||
# Set the expiration delay for the secure cookie with the JWT token
|
||||
# Delay is in millisecond e.g. 7 days is 1000*60*60*24*7
|
||||
SESSION_EXPIRY=(1000 * 60 * 60 * 24) * 7
|
||||
|
|
|
@ -65,6 +65,11 @@ const userSchema = mongoose.Schema(
|
|||
unique: true,
|
||||
sparse: true
|
||||
},
|
||||
openidId: {
|
||||
type: String,
|
||||
unique: true,
|
||||
sparse: true
|
||||
},
|
||||
plugins: {
|
||||
type: Array,
|
||||
default: []
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
"dotenv": "^16.0.3",
|
||||
"eslint": "^8.41.0",
|
||||
"express": "^4.18.2",
|
||||
"express-session": "^1.17.3",
|
||||
"googleapis": "^118.0.0",
|
||||
"handlebars": "^4.7.7",
|
||||
"html": "^1.0.0",
|
||||
|
@ -52,6 +53,7 @@
|
|||
"mongoose": "^7.1.1",
|
||||
"nodemailer": "^6.9.1",
|
||||
"openai": "^3.2.1",
|
||||
"openid-client": "^5.4.2",
|
||||
"passport": "^0.6.0",
|
||||
"passport-facebook": "^3.0.0",
|
||||
"passport-google-oauth20": "^2.0.0",
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const express = require('express');
|
||||
const session = require('express-session');
|
||||
const connectDb = require('../lib/db/connectDb');
|
||||
const migrateDb = require('../lib/db/migrateDb');
|
||||
const indexSync = require('../lib/db/indexSync');
|
||||
|
@ -41,6 +42,15 @@ config.validate(); // Validate the config
|
|||
if (process.env.FACEBOOK_CLIENT_ID && process.env.FACEBOOK_CLIENT_SECRET) {
|
||||
require('../strategies/facebookStrategy');
|
||||
}
|
||||
if (process.env.OPENID_CLIENT_ID && process.env.OPENID_CLIENT_SECRET && process.env.OPENID_ISSUER && process.env.OPENID_SCOPE && process.env.OPENID_SESSION_SECRET) {
|
||||
app.use(session({
|
||||
secret: process.env.OPENID_SESSION_SECRET,
|
||||
resave: false,
|
||||
saveUninitialized: false
|
||||
}));
|
||||
app.use(passport.session());
|
||||
require('../strategies/openidStrategy');
|
||||
}
|
||||
app.use('/oauth', routes.oauth);
|
||||
// api endpoint
|
||||
app.use('/api/auth', routes.auth);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
const request = require('supertest');
|
||||
const express = require('express');
|
||||
const routes = require('../');
|
||||
|
||||
const app = express();
|
||||
app.use('/api/config', routes.config);
|
||||
|
||||
|
@ -9,6 +8,12 @@ afterEach(() => {
|
|||
delete process.env.APP_TITLE;
|
||||
delete process.env.GOOGLE_CLIENT_ID;
|
||||
delete process.env.GOOGLE_CLIENT_SECRET;
|
||||
delete process.env.OPENID_CLIENT_ID;
|
||||
delete process.env.OPENID_CLIENT_SECRET;
|
||||
delete process.env.OPENID_ISSUER;
|
||||
delete process.env.OPENID_SESSION_SECRET;
|
||||
delete process.env.VITE_OPENID_LABEL;
|
||||
delete process.env.VITE_OPENID_URL;
|
||||
delete process.env.DOMAIN_SERVER;
|
||||
delete process.env.ALLOW_REGISTRATION;
|
||||
});
|
||||
|
@ -21,6 +26,12 @@ describe.skip('GET /', () => {
|
|||
process.env.APP_TITLE = 'Test Title';
|
||||
process.env.GOOGLE_CLIENT_ID = 'Test Google Client Id';
|
||||
process.env.GOOGLE_CLIENT_SECRET = 'Test Google Client Secret';
|
||||
process.env.OPENID_CLIENT_ID= 'Test OpenID Id';
|
||||
process.env.OPENID_CLIENT_SECRET= 'Test OpenID Secret';
|
||||
process.env.OPENID_ISSUER= 'Test OpenID Issuer';
|
||||
process.env.OPENID_SESSION_SECRET= 'Test Secret';
|
||||
process.env.VITE_OPENID_LABEL= 'Test OpenID';
|
||||
process.env.VITE_OPENID_URL= 'http://test-server.com';
|
||||
process.env.DOMAIN_SERVER = 'http://test-server.com';
|
||||
process.env.ALLOW_REGISTRATION = 'true';
|
||||
|
||||
|
@ -30,8 +41,11 @@ describe.skip('GET /', () => {
|
|||
expect(response.body).toEqual({
|
||||
appTitle: 'Test Title',
|
||||
googleLoginEnabled: true,
|
||||
openidLoginEnabled: true,
|
||||
openidLabel: 'Test OpenID',
|
||||
openidUrl: 'http://test-server.com',
|
||||
serverDomain: 'http://test-server.com',
|
||||
registrationEnabled: 'true',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,10 +5,13 @@ router.get('/', async function (req, res) {
|
|||
try {
|
||||
const appTitle = process.env.APP_TITLE || 'LibreChat';
|
||||
const googleLoginEnabled = !!process.env.GOOGLE_CLIENT_ID && !!process.env.GOOGLE_CLIENT_SECRET;
|
||||
const openidLoginEnabled = !!process.env.OPENID_CLIENT_ID && !!process.env.OPENID_CLIENT_SECRET && !!process.env.OPENID_ISSUER && !!process.env.OPENID_SESSION_SECRET;
|
||||
const openidLabel = process.env.VITE_OPENID_LABEL || 'Login with OpenID';
|
||||
const openidUrl = process.env.VITE_OPENID_URL;
|
||||
const serverDomain = process.env.DOMAIN_SERVER || 'http://localhost:3080';
|
||||
const registrationEnabled = process.env.ALLOW_REGISTRATION || true;
|
||||
|
||||
return res.status(200).send({appTitle, googleLoginEnabled, serverDomain, registrationEnabled});
|
||||
return res.status(200).send({appTitle, googleLoginEnabled, openidLoginEnabled, openidLabel, openidUrl, serverDomain, registrationEnabled});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return res.status(500).send({error: err.message});
|
||||
|
|
|
@ -62,4 +62,29 @@ router.get(
|
|||
}
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/openid',
|
||||
passport.authenticate('openid', {
|
||||
session: false
|
||||
})
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/openid/callback',
|
||||
passport.authenticate('openid', {
|
||||
failureRedirect: `${domains.client}/login`,
|
||||
failureMessage: true,
|
||||
session: false
|
||||
}),
|
||||
(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;
|
||||
|
|
123
api/strategies/openidStrategy.js
Normal file
123
api/strategies/openidStrategy.js
Normal file
|
@ -0,0 +1,123 @@
|
|||
const passport = require('passport');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const { Issuer, Strategy: OpenIDStrategy } = require('openid-client');
|
||||
const axios = require('axios');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const config = require('../../config/loader');
|
||||
const domains = config.domains;
|
||||
|
||||
const User = require('../models/User');
|
||||
|
||||
let crypto;
|
||||
try {
|
||||
crypto = require('node:crypto');
|
||||
} catch (err) {
|
||||
console.error('crypto support is disabled!');
|
||||
}
|
||||
|
||||
const downloadImage = async (url, imagePath, accessToken) => {
|
||||
try {
|
||||
const response = await axios.get(url, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${accessToken}`
|
||||
},
|
||||
responseType: 'arraybuffer'
|
||||
});
|
||||
|
||||
fs.mkdirSync(path.dirname(imagePath), { recursive: true });
|
||||
fs.writeFileSync(imagePath, response.data);
|
||||
|
||||
const fileName = path.basename(imagePath);
|
||||
|
||||
return `/images/openid/${fileName}`;
|
||||
} catch (error) {
|
||||
console.error(`Error downloading image at URL "${url}": ${error}`);
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
Issuer.discover(process.env.OPENID_ISSUER)
|
||||
.then(issuer => {
|
||||
const client = new issuer.Client({
|
||||
client_id: process.env.OPENID_CLIENT_ID,
|
||||
client_secret: process.env.OPENID_CLIENT_SECRET,
|
||||
redirect_uris: [domains.server + process.env.OPENID_CALLBACK_URL]
|
||||
});
|
||||
|
||||
const openidLogin = new OpenIDStrategy(
|
||||
{
|
||||
client,
|
||||
params: {
|
||||
scope: process.env.OPENID_SCOPE
|
||||
}
|
||||
},
|
||||
async (tokenset, userinfo, done) => {
|
||||
try {
|
||||
let user = await User.findOne({ openidId: userinfo.sub });
|
||||
|
||||
if (!user) {
|
||||
user = await User.findOne({ email: userinfo.email });
|
||||
}
|
||||
|
||||
let fullName = '';
|
||||
if (userinfo.given_name && userinfo.family_name) {
|
||||
fullName = userinfo.given_name + ' ' + userinfo.family_name;
|
||||
} else if (userinfo.given_name) {
|
||||
fullName = userinfo.given_name;
|
||||
} else if (userinfo.family_name) {
|
||||
fullName = userinfo.family_name;
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
user = new User({
|
||||
provider: 'openid',
|
||||
openidId: userinfo.sub,
|
||||
username: userinfo.given_name || '',
|
||||
email: userinfo.email || '',
|
||||
emailVerified: userinfo.email_verified || false,
|
||||
name: fullName
|
||||
});
|
||||
} else {
|
||||
user.provider = 'openid';
|
||||
user.openidId = userinfo.sub;
|
||||
user.username = userinfo.given_name || '';
|
||||
user.name = fullName;
|
||||
}
|
||||
|
||||
if (userinfo.picture) {
|
||||
const imageUrl = userinfo.picture;
|
||||
|
||||
let fileName;
|
||||
if (crypto) {
|
||||
const hash = crypto.createHash('sha256');
|
||||
hash.update(userinfo.sub);
|
||||
fileName = hash.digest('hex') + '.png';
|
||||
} else {
|
||||
fileName = userinfo.sub + '.png';
|
||||
}
|
||||
|
||||
const imagePath = path.join(__dirname, '..', '..', 'client', 'public', 'images', 'openid', fileName);
|
||||
|
||||
const imagePathOrEmpty = await downloadImage(imageUrl, imagePath, tokenset.access_token);
|
||||
|
||||
user.avatar = imagePathOrEmpty;
|
||||
} else {
|
||||
user.avatar = '';
|
||||
}
|
||||
|
||||
await user.save();
|
||||
|
||||
done(null, user);
|
||||
} catch (err) {
|
||||
done(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
passport.use('openid', openidLogin);
|
||||
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
});
|
3
client/env.d.ts
vendored
3
client/env.d.ts
vendored
|
@ -3,6 +3,9 @@ interface ImportMetaEnv {
|
|||
readonly VITE_SERVER_URL_DEV: string;
|
||||
readonly VITE_SERVER_URL_PROD: string;
|
||||
readonly VITE_SHOW_GOOGLE_LOGIN_OPTION: string;
|
||||
readonly ALLOW_OPENID: string;
|
||||
readonly VITE_OPENID_LABEL: string;
|
||||
readonly VITE_OPENID_URL: string;
|
||||
readonly VITE_CLIENT_URL_DEV: string;
|
||||
readonly VITE_CLIENT_URL_PROD: string;
|
||||
}
|
||||
|
|
|
@ -78,6 +78,36 @@ function Login() {
|
|||
</div>
|
||||
</>
|
||||
)}
|
||||
{startupConfig?.openidLoginEnabled && (
|
||||
<>
|
||||
<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"
|
||||
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/openid`}
|
||||
>
|
||||
{startupConfig.openidUrl ? (
|
||||
<img src={startupConfig.openidUrl} 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>
|
||||
)}
|
||||
<p>{startupConfig.openidLabel}</p>
|
||||
</a>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -312,6 +312,36 @@ function Registration() {
|
|||
</div>
|
||||
</>
|
||||
)}
|
||||
{startupConfig?.openidLoginEnabled && (
|
||||
<>
|
||||
<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"
|
||||
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/openid`}
|
||||
>
|
||||
{startupConfig.openidUrl ? (
|
||||
<img src={startupConfig.openidUrl} 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>
|
||||
)}
|
||||
<p>{startupConfig.openidLabel}</p>
|
||||
</a>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -23,6 +23,9 @@ const setup = ({
|
|||
isError: false,
|
||||
data: {
|
||||
googleLoginEnabled: true,
|
||||
openidLoginEnabled: true,
|
||||
openidLabel: 'Test OpenID',
|
||||
openidUrl: 'http://test-server.com',
|
||||
registrationEnabled: true,
|
||||
serverDomain: 'mock-server'
|
||||
}
|
||||
|
|
|
@ -23,6 +23,9 @@ const setup = ({
|
|||
isError: false,
|
||||
data: {
|
||||
googleLoginEnabled: true,
|
||||
openidLoginEnabled: true,
|
||||
openidLabel: 'Test OpenID',
|
||||
openidUrl: 'http://test-server.com',
|
||||
registrationEnabled: true,
|
||||
serverDomain: 'mock-server'
|
||||
}
|
||||
|
|
|
@ -237,6 +237,9 @@ export type TResetPassword = {
|
|||
export type TStartupConfig = {
|
||||
appTitle: boolean;
|
||||
googleLoginEnabled: boolean;
|
||||
openidLoginEnabled: boolean;
|
||||
openidLabel: string;
|
||||
openidUrl: string;
|
||||
serverDomain: string;
|
||||
registrationEnabled: boolean;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ services:
|
|||
- ./.env.development:/app/.env.development
|
||||
- ./.env.production:/app/.env.production
|
||||
- /app/api/node_modules
|
||||
- ./images:/app/client/public/images
|
||||
mongodb:
|
||||
container_name: chat-mongodb
|
||||
ports:
|
||||
|
|
|
@ -17,22 +17,18 @@ DOMAIN_CLIENT=http://localhost:3080
|
|||
|
||||
*Please Note: If you are wanting this to work in development mode, you will need to create a file called `.env.development` in the root directory and set `DOMAIN_CLIENT` to `http://localhost:3090` or whatever port is provided by vite when runnning `npm run frontend-dev`*
|
||||
|
||||
The first time you run the application, you should register a new account by clicking the "Sign up" link on the login page. The first account registered will receive an admin role. The admin account does not currently have extended functionality, but is valuable should you choose to create an admin dashboard for user management.
|
||||
Important: When you run the app for the first time, you need to create a new account by clicking on "Sign up" on the login page. The first account you make will be the admin account. The admin account doesn't have any special features right now, but it might be useful if you want to make an admin dashboard to manage other users later.
|
||||
|
||||
## **Migrating Previous Conversations and Presets to new User Account**
|
||||
⚠️ **__For the first time, you should use a local account (email and password) to sign up and log in.__**
|
||||
|
||||
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.
|
||||
|
||||
⚠️**IMPORTANT**: 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. You should register and login with a local account (email and password) for the first time.
|
||||
---
|
||||
|
||||
## **OAuth2/Social Login**
|
||||
|
||||
The application is setup to support OAuth2/Social Login with Google. All of the code is in place for Facebook login as well, but this has not been tested because the setup process with Facebook was honestly just too painful for me to deal with. I plan to add support for other OAuth2 providers including Github and Discord at a later time.
|
||||
## How to Set Up Google Authentication
|
||||
|
||||
To enable Google login, you must create an application in the [Google Cloud Console](https://cloud.google.com) and provide the client ID and client secret in the `/.env` file.
|
||||
|
||||
### *Instructions for setting up Google login are provided below.*
|
||||
```
|
||||
1. Go to "APIs and Services" in your Google Cloud account and click on "Credentials".
|
||||
2. Click on "Configure consent screen" and select "External" as the user type.
|
||||
3. Add "profile", "email" and "openid" as the scopes for your app. These are the first three checkboxes when you click on "Add or remove scopes".
|
||||
|
@ -44,7 +40,33 @@ To enable Google login, you must create an application in the [Google Cloud Cons
|
|||
9. Click on "Create" and copy your client ID and client secret.
|
||||
10. Paste them into your /.env file.
|
||||
11. Enable the feature in the /.env file
|
||||
|
||||
---
|
||||
|
||||
## How to Set Up OpenID Authentication with Azure AD
|
||||
|
||||
1. Go to the Azure Portal and sign in with your account.
|
||||
2. In the search box, type Azure Active Directory and click on it.
|
||||
3. On the left menu, click on App registrations and then on New registration.
|
||||
4. Give your app a name and select Web as the platform type.
|
||||
5. In the Redirect URI field, enter https://fqdn/oauth/openid/callback and click on Register.
|
||||
6. You will see an Overview page with some information about your app. Copy the Application (client) ID and the Directory (tenant) ID and save them somewhere.
|
||||
7. On the left menu, click on Authentication and check the boxes for Access tokens and ID tokens under Implicit grant and hybrid flows.
|
||||
8. On the left menu, click on Certificates & Secrets and then on New client secret. Give your secret a name and an expiration date and click on Add.
|
||||
9. You will see a Value column with your secret. Copy it and save it somewhere. Don't share it with anyone!
|
||||
10. Open the .env file in your project folder and add the following variables with the values you copied:
|
||||
|
||||
```
|
||||
OPENID_CLIENT_ID=Your Application (client) ID
|
||||
OPENID_CLIENT_SECRET=Your client secret
|
||||
OPENID_ISSUER=https://login.microsoftonline.com/Your Directory (tenant ID)/v2.0/
|
||||
OPENID_SESSION_SECRET=Any random string
|
||||
OPENID_SCOPE=openid profile email
|
||||
OPENID_CALLBACK_URL=/oauth/openid/callback
|
||||
```
|
||||
11. Save the .env file and you're done! You have successfully set up OpenID authentication with Azure AD for your app.
|
||||
|
||||
---
|
||||
|
||||
## **Email and Password Reset**
|
||||
|
||||
|
@ -58,6 +80,11 @@ To disable or re-enable registration, open up the root `.env` file and set `ALLO
|
|||
|
||||
If you previously implemented your own user system using the original scaffolding that was provided, you will no longer see conversations and presets by switching to the new user system. This is because of a design flaw in the scaffolding implementation that was problematic for the inclusion of social login.
|
||||
|
||||
### 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.
|
||||
|
||||
---
|
||||
|
||||
## [Go Back to ReadMe](../../README.md)
|
||||
|
|
88398
package-lock.json
generated
88398
package-lock.json
generated
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue