diff --git a/app/src/Server.js b/app/src/Server.js index e28bb0c9..2a175686 100644 --- a/app/src/Server.js +++ b/app/src/Server.js @@ -17,6 +17,7 @@ dependencies: { cors : https://www.npmjs.com/package/cors crypto-js : https://www.npmjs.com/package/crypto-js express : https://www.npmjs.com/package/express + express-openid-connect : https://www.npmjs.com/package/express-openid-connect httpolyglot : https://www.npmjs.com/package/httpolyglot jsonwebtoken : https://www.npmjs.com/package/jsonwebtoken mediasoup : https://www.npmjs.com/package/mediasoup @@ -46,6 +47,7 @@ dependencies: { */ const express = require('express'); +const { auth, requiresAuth } = require('express-openid-connect'); const cors = require('cors'); const compression = require('compression'); const https = require('httpolyglot'); @@ -171,6 +173,9 @@ if (config.chatGPT.enabled) { } } +// OpenID Connect +const OIDC = config.oidc ? config.oidc : { enabled: false }; + // directory const dir = { public: path.join(__dirname, '../../', 'public'), @@ -247,6 +252,15 @@ if (!announcedAddress && IPv4 === '0.0.0.0') { startServer(); } +// Custom middleware function for OIDC authentication +function OIDCAuth(req, res, next) { + if (OIDC.enabled) { + requiresAuth()(req, res, next); // Apply requiresAuth() middleware conditionally + } else { + next(); + } +} + function startServer() { // Start the app app.use(cors(corsOptions)); @@ -298,6 +312,33 @@ function startServer() { } }); + // OpenID Connect + if (OIDC.enabled) { + try { + app.use(auth(OIDC.config)); + } catch (err) { + log.error(err); + process.exit(1); + } + } + + // Route to display user information + app.get('/profile', OIDCAuth, (req, res) => { + const user = OIDC.enabled ? req.oidc.user : { message: 'Profile not found!' }; + res.json(user); // Send user information as JSON + }); + + // Authentication Callback Route + app.get('/auth/callback', (req, res, next) => { + next(); // Let express-openid-connect handle this route + }); + + // Logout Route + app.get('/logout', (req, res) => { + if (OIDC.enabled) req.logout(); + res.redirect('/'); // Redirect to the home page after logout + }); + // UI buttons configuration app.get('/config', (req, res) => { res.status(200).json({ message: config.ui ? config.ui.buttons : false }); @@ -309,7 +350,7 @@ function startServer() { }); // main page - app.get(['/'], (req, res) => { + app.get(['/'], OIDCAuth, (req, res) => { //log.debug('/ - hostCfg ----->', hostCfg); if ((hostCfg.protected && !hostCfg.authenticated) || authHost.isRoomActive()) { const ip = getIP(req); @@ -326,7 +367,7 @@ function startServer() { }); // set new room name and join - app.get(['/newroom'], (req, res) => { + app.get(['/newroom'], OIDCAuth, (req, res) => { //log.info('/newroom - hostCfg ----->', hostCfg); if ((hostCfg.protected && !hostCfg.authenticated) || authHost.isRoomActive()) { @@ -344,7 +385,7 @@ function startServer() { }); // Handle Direct join room with params - app.get('/join/', async (req, res) => { + app.get('/join/', OIDCAuth, async (req, res) => { if (Object.keys(req.query).length > 0) { //log.debug('/join/params - hostCfg ----->', hostCfg); @@ -403,7 +444,7 @@ function startServer() { }); // join room by id - app.get('/join/:roomId', (req, res) => { + app.get('/join/:roomId', OIDCAuth, (req, res) => { //log.debug('/join/room - hostCfg ----->', hostCfg); if (hostCfg.authenticated || authHost.isRoomActive()) { res.sendFile(views.room); @@ -733,6 +774,7 @@ function startServer() { chatGPT_enabled: config.chatGPT.enabled, configUI: config.ui, serverRec: config?.server?.recording, + oidc: OIDC, }; } diff --git a/app/src/config.template.js b/app/src/config.template.js index 47d36e2d..3981c738 100644 --- a/app/src/config.template.js +++ b/app/src/config.template.js @@ -91,6 +91,36 @@ module.exports = { key: 'mirotalksfu_jwt_secret', exp: '1h', }, + oidc: { + /* + OIDC stands for OpenID Connect, which is an authentication protocol built on top of OAuth 2.0. + It provides a simple identity layer on the OAuth 2.0 protocol, allowing clients to verify the identity of the end-user + based on the authentication performed by an authorization server. + How to configure your own Provider: + 1. Sign up for an account at https://auth0.com. + 2. Navigate to https://manage.auth0.com/ to create a new application tailored to your specific requirements. + For those seeking an open-source solution, check out: https://github.com/panva/node-oidc-provider + */ + enabled: false, + config: { + issuerBaseURL: 'issuerBaseURL', + clientID: 'clientID', + clientSecret: 'clientSecret', + baseURL: `http://localhost:${process.env.PORT ? process.env.PORT : 3010}`, + secret: 'mirotalksfu-oidc-secret', + authorizationParams: { + response_type: 'code', + scope: 'openid profile email', + }, + authRequired: false, // Set to true if authentication is required for all routes + auth0Logout: true, // Set to true to enable logout with Auth0 + routes: { + callback: '/auth/callback', // Indicating the endpoint where your application will handle the callback from the authentication provider after a user has been authenticated. + login: false, // Dedicated route in your application for user login. + logout: '/logout', // Indicating the endpoint where your application will handle user logout requests. + }, + }, + }, host: { /* Host Protection (default: false) diff --git a/package.json b/package.json index 04d3f4e5..7b257346 100644 --- a/package.json +++ b/package.json @@ -50,13 +50,14 @@ "cors": "2.8.5", "crypto-js": "4.2.0", "express": "4.19.2", + "express-openid-connect": "^2.17.1", "httpolyglot": "0.1.2", "jsonwebtoken": "^9.0.2", "mediasoup": "3.14.3", "mediasoup-client": "3.7.7", "ngrok": "^5.0.0-beta.2", "nodemailer": "^6.9.13", - "openai": "^4.39.0", + "openai": "^4.40.0", "qs": "6.12.1", "socket.io": "4.7.5", "swagger-ui-express": "5.0.0",