From 1abed5480eeab2181de7adfdc569868453b52212 Mon Sep 17 00:00:00 2001 From: yash-rajpal Date: Wed, 10 Jun 2026 16:22:01 +0530 Subject: [PATCH 1/4] wip: setting to toggle between meteor and passport OAuth --- apps/meteor/app/dolphin/server/lib.ts | 22 ++++++++++++--- apps/meteor/app/drupal/server/lib.ts | 16 +++++++++-- apps/meteor/app/gitlab/server/lib.ts | 14 +++++++++- apps/meteor/app/nextcloud/server/lib.ts | 27 ++++++++++++++----- apps/meteor/app/wordpress/server/lib.ts | 14 ++++++++-- .../server/configuration/configurePassport.ts | 18 ++++++++++--- .../lib/oauth/addPassportCustomOAuth.ts | 10 ++++++- .../lib/oauth/configureOAuthServices.ts | 11 +++++++- apps/meteor/server/settings/oauth.ts | 10 +++++++ packages/i18n/src/locales/en.i18n.json | 4 ++- .../web-ui-registration/src/LoginServices.tsx | 9 ++++++- .../src/LoginServicesButton.tsx | 6 +++-- 12 files changed, 137 insertions(+), 24 deletions(-) diff --git a/apps/meteor/app/dolphin/server/lib.ts b/apps/meteor/app/dolphin/server/lib.ts index 77622fe43edab..a4c8e700bc0ad 100644 --- a/apps/meteor/app/dolphin/server/lib.ts +++ b/apps/meteor/app/dolphin/server/lib.ts @@ -7,6 +7,7 @@ import _ from 'underscore'; import { callbacks } from '../../../server/lib/callbacks'; import { beforeCreateUserCallback } from '../../../server/lib/callbacks/beforeCreateUserCallback'; import { addPassportCustomOAuth } from '../../../server/lib/oauth/addPassportCustomOAuth'; +import { CustomOAuth } from '../../custom-oauth/server/custom_oauth_server'; import { settings } from '../../settings/server'; const config: Partial = { @@ -22,6 +23,8 @@ const config: Partial = { accessTokenParam: 'access_token', }; +const Dolphin = new CustomOAuth('dolphin', config); + function DolphinOnCreateUser(options: any, user?: IUser) { // TODO: callbacks Fix this if (user?.services?.dolphin?.NickName) { @@ -46,14 +49,27 @@ const configureDolphinOAuth = () => { return; } - addPassportCustomOAuth('dolphin', { ...config, serverURL, clientId, clientSecret }); + const completeConfig = { ...config, serverURL, clientId, clientSecret }; + + if (settings.get('Accounts_OAuth_Flow_Engine') === 'passport') { + addPassportCustomOAuth('dolphin', completeConfig); + return; + } + + Dolphin.configure(completeConfig); }; Meteor.startup(async () => { - const updateConfig = () => _.debounce(configureDolphinOAuth, 300); + const updateConfig = _.debounce(configureDolphinOAuth, 300); settings.watchMultiple( - ['Accounts_OAuth_Dolphin', 'Accounts_OAuth_Dolphin_URL', 'Accounts_OAuth_Dolphin_id', 'Accounts_OAuth_Dolphin_secret'], + [ + 'Accounts_OAuth_Dolphin', + 'Accounts_OAuth_Dolphin_URL', + 'Accounts_OAuth_Dolphin_id', + 'Accounts_OAuth_Dolphin_secret', + 'Accounts_OAuth_Flow_Engine', + ], updateConfig, ); diff --git a/apps/meteor/app/drupal/server/lib.ts b/apps/meteor/app/drupal/server/lib.ts index a4f71bd81696f..cb02b6fd05b5a 100644 --- a/apps/meteor/app/drupal/server/lib.ts +++ b/apps/meteor/app/drupal/server/lib.ts @@ -4,9 +4,11 @@ import passport from 'passport'; import _ from 'underscore'; import { addPassportCustomOAuth } from '../../../server/lib/oauth/addPassportCustomOAuth'; +import { CustomOAuth } from '../../custom-oauth/server/custom_oauth_server'; import { settings } from '../../settings/server'; const config: Partial = { + serverURL: '', identityPath: '/oauth2/UserInfo', authorizePath: '/oauth2/authorize', tokenPath: '/oauth2/token', @@ -21,6 +23,8 @@ const config: Partial = { accessTokenParam: 'access_token', }; +const Drupal = new CustomOAuth('drupal', config); + const configureDrupalOAuth = () => { passport.unuse('drupal'); const enabled = settings.get('Accounts_OAuth_Drupal'); @@ -36,14 +40,22 @@ const configureDrupalOAuth = () => { return; } - addPassportCustomOAuth('drupal', { ...config, serverURL, clientId, clientSecret }); + const isPassportFlowEnabled = settings.get('Accounts_OAuth_Flow_Engine') === 'passport'; + const completeConfig = { ...config, serverURL, clientId, clientSecret }; + + if (isPassportFlowEnabled) { + addPassportCustomOAuth('drupal', completeConfig); + return; + } + + Drupal.configure(completeConfig); }; Meteor.startup(() => { const updateConfig = _.debounce(configureDrupalOAuth, 300); settings.watchMultiple( - ['Accounts_OAuth_Drupal', 'API_Drupal_URL', 'Accounts_OAuth_Drupal_id', 'Accounts_OAuth_Drupal_secret'], + ['Accounts_OAuth_Drupal', 'API_Drupal_URL', 'Accounts_OAuth_Drupal_id', 'Accounts_OAuth_Drupal_secret', 'Accounts_OAuth_Flow_Engine'], updateConfig, ); }); diff --git a/apps/meteor/app/gitlab/server/lib.ts b/apps/meteor/app/gitlab/server/lib.ts index 3df1c1ded5496..95ade4b36d03b 100644 --- a/apps/meteor/app/gitlab/server/lib.ts +++ b/apps/meteor/app/gitlab/server/lib.ts @@ -4,6 +4,7 @@ import passport from 'passport'; import _ from 'underscore'; import { addPassportCustomOAuth } from '../../../server/lib/oauth/addPassportCustomOAuth'; +import { CustomOAuth } from '../../custom-oauth/server/custom_oauth_server'; import { settings } from '../../settings/server'; const config: Partial = { @@ -18,6 +19,8 @@ const config: Partial = { accessTokenParam: 'access_token', }; +const Gitlab = new CustomOAuth('gitlab', config); + const configureGitlabOAuth = () => { passport.unuse('gitlab'); @@ -36,7 +39,15 @@ const configureGitlabOAuth = () => { return; } - addPassportCustomOAuth('gitlab', { ...config, clientId, clientSecret, serverURL, identityPath, mergeUsers }); + const isPassportFlowEnabled = settings.get('Accounts_OAuth_Flow_Engine') === 'passport'; + const completeConfig = { ...config, clientId, clientSecret, serverURL, identityPath, mergeUsers }; + + if (isPassportFlowEnabled) { + addPassportCustomOAuth('gitlab', completeConfig); + return; + } + + Gitlab.configure(completeConfig); }; Meteor.startup(() => { @@ -50,6 +61,7 @@ Meteor.startup(() => { 'Accounts_OAuth_Gitlab_secret', 'Accounts_OAuth_Gitlab_identity_path', 'Accounts_OAuth_Gitlab_merge_users', + 'Accounts_OAuth_Flow_Engine', ], updateConfig, ); diff --git a/apps/meteor/app/nextcloud/server/lib.ts b/apps/meteor/app/nextcloud/server/lib.ts index fc2b592b7c1ad..0d59e3a2f3cc8 100644 --- a/apps/meteor/app/nextcloud/server/lib.ts +++ b/apps/meteor/app/nextcloud/server/lib.ts @@ -2,9 +2,11 @@ import type { OAuthConfiguration } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; import { addPassportCustomOAuth } from '../../../server/lib/oauth/addPassportCustomOAuth'; +import { CustomOAuth } from '../../custom-oauth/server/custom_oauth_server'; import { settings } from '../../settings/server/cached'; const NEXTCLOUD_PATHS = { + serverURL: '', tokenPath: '/index.php/apps/oauth2/api/v1/token', tokenSentVia: 'header' as OAuthConfiguration['tokenSentVia'], authorizePath: '/index.php/apps/oauth2/authorize', @@ -16,6 +18,8 @@ const NEXTCLOUD_PATHS = { }, }; +const Nextcloud = new CustomOAuth('nextcloud', NEXTCLOUD_PATHS); + function configureNextcloudOAuth(): void { const enabled = settings.get('Accounts_OAuth_Nextcloud'); if (!enabled) { @@ -30,17 +34,26 @@ function configureNextcloudOAuth(): void { return; } - addPassportCustomOAuth('nextcloud', { - ...NEXTCLOUD_PATHS, - serverURL, - clientId, - clientSecret, - }); + const config = { ...NEXTCLOUD_PATHS, serverURL, clientId, clientSecret }; + const isPassportFlowEnabled = settings.get('Accounts_OAuth_Flow_Engine') === 'passport'; + + if (isPassportFlowEnabled) { + addPassportCustomOAuth('nextcloud', config); + return; + } + + Nextcloud.configure(config); } Meteor.startup(() => { settings.watchMultiple( - ['Accounts_OAuth_Nextcloud', 'Accounts_OAuth_Nextcloud_URL', 'Accounts_OAuth_Nextcloud_id', 'Accounts_OAuth_Nextcloud_secret'], + [ + 'Accounts_OAuth_Nextcloud', + 'Accounts_OAuth_Nextcloud_URL', + 'Accounts_OAuth_Nextcloud_id', + 'Accounts_OAuth_Nextcloud_secret', + 'Accounts_OAuth_Flow_Engine', + ], configureNextcloudOAuth, ); }); diff --git a/apps/meteor/app/wordpress/server/lib.ts b/apps/meteor/app/wordpress/server/lib.ts index eb1ce2c01ceb2..6cf02496457b8 100644 --- a/apps/meteor/app/wordpress/server/lib.ts +++ b/apps/meteor/app/wordpress/server/lib.ts @@ -5,6 +5,7 @@ import passport from 'passport'; import _ from 'underscore'; import { addPassportCustomOAuth } from '../../../server/lib/oauth/addPassportCustomOAuth'; +import { CustomOAuth } from '../../custom-oauth/server/custom_oauth_server'; import { settings } from '../../settings/server'; const config: Partial = { @@ -20,6 +21,8 @@ const config: Partial = { const serviceKey = 'wordpress'; +const WordPress = new CustomOAuth(serviceKey, config); + const fillSettings = _.debounce(async (): Promise => { config.serverURL = settings.get('API_Wordpress_URL'); if (!config.serverURL) { @@ -66,7 +69,13 @@ const fillSettings = _.debounce(async (): Promise => { break; } - addPassportCustomOAuth(serviceKey, config); + const isPassportFlowEnabled = settings.get('Accounts_OAuth_Flow_Engine') === 'passport'; + + if (isPassportFlowEnabled) { + addPassportCustomOAuth(serviceKey, config); + } else { + WordPress.configure(config); + } const enabled = settings.get('Accounts_OAuth_Wordpress'); if (enabled) { @@ -86,5 +95,6 @@ const fillSettings = _.debounce(async (): Promise => { }, 1000); Meteor.startup(() => { - return settings.watchByRegex(/(API\_Wordpress\_URL)?(Accounts\_OAuth\_Wordpress\_)?/, () => fillSettings()); + settings.watchByRegex(/(API\_Wordpress\_URL)?(Accounts\_OAuth\_Wordpress\_)?/, () => fillSettings()); + settings.watch('Accounts_OAuth_Flow_Engine', () => fillSettings()); }); diff --git a/apps/meteor/server/configuration/configurePassport.ts b/apps/meteor/server/configuration/configurePassport.ts index 46adff48c78de..3db9904c7d5ce 100644 --- a/apps/meteor/server/configuration/configurePassport.ts +++ b/apps/meteor/server/configuration/configurePassport.ts @@ -22,6 +22,16 @@ export const oAuthRouter = router(); const oAuthApp = express(); oAuthApp.set('trust proxy', true); +const configureOAuth = (settings: ICachedSettings) => { + if (settings.get('Accounts_OAuth_Flow_Engine') !== 'passport') { + return; + } + + const services = getOAuthServices(settings); + const oauthServiceConfigs = createOAuthServiceConfig(settings, services); + configureOAuthServices(oauthServiceConfigs, settings); +}; + export const configurePassport = (settings: ICachedSettings) => { const { client } = MongoInternals.defaultRemoteCollectionDriver().mongo; @@ -84,9 +94,11 @@ export const configurePassport = (settings: ICachedSettings) => { }); settings.watchByRegex(/^(Accounts_OAuth_)[a-z0-9_]+$/i, () => { - const services = getOAuthServices(settings); - const oauthServiceConfigs = createOAuthServiceConfig(settings, services); - configureOAuthServices(oauthServiceConfigs, settings); + configureOAuth(settings); + }); + + settings.watch('Accounts_OAuth_Flow_Engine', () => { + configureOAuth(settings); }); WebApp.rawConnectHandlers.use(oAuthApp); diff --git a/apps/meteor/server/lib/oauth/addPassportCustomOAuth.ts b/apps/meteor/server/lib/oauth/addPassportCustomOAuth.ts index e26f51bdf9c72..4ec8868c9993f 100644 --- a/apps/meteor/server/lib/oauth/addPassportCustomOAuth.ts +++ b/apps/meteor/server/lib/oauth/addPassportCustomOAuth.ts @@ -47,7 +47,15 @@ export const addPassportCustomOAuth = (serviceName: string, config: Partial { + const isPassportFlowEnabled = settings.get('Accounts_OAuth_Flow_Engine') === 'passport'; + if (isPassportFlowEnabled) { + next(); + } else { + next('router'); + } + }, + passport.authenticate(serviceName, { failureRedirect: '/login', failWithError: true, keepSessionInfo: true }), passportOAuthCallback(siteUrl), ); }; diff --git a/apps/meteor/server/lib/oauth/configureOAuthServices.ts b/apps/meteor/server/lib/oauth/configureOAuthServices.ts index 5ff0b202a2e89..9ab415573fd42 100644 --- a/apps/meteor/server/lib/oauth/configureOAuthServices.ts +++ b/apps/meteor/server/lib/oauth/configureOAuthServices.ts @@ -80,7 +80,16 @@ export const configureOAuthServices = (oauthServiceConfig: OAuthServiceConfig[], ); oAuthRouter.get( `/_oauth/${config.provider}`, - passport.authenticate(config.provider, { failureRedirect: '/login', failureFlash: true, failWithError: true, keepSessionInfo: true }), + (_req, _res, next) => { + console.log('In check middleware'); + const isPassportFlowEnabled = settings.get('Accounts_OAuth_Flow_Engine') === 'passport'; + if (isPassportFlowEnabled) { + next(); + } else { + next('router'); + } + }, + passport.authenticate(config.provider, { failureRedirect: '/login', failWithError: true, keepSessionInfo: true }), passportOAuthCallback(siteUrl), ); }); diff --git a/apps/meteor/server/settings/oauth.ts b/apps/meteor/server/settings/oauth.ts index 8ee583614882b..9556f6c562958 100644 --- a/apps/meteor/server/settings/oauth.ts +++ b/apps/meteor/server/settings/oauth.ts @@ -4,6 +4,16 @@ import { settingsRegistry } from '../../app/settings/server'; export const createOauthSettings = () => settingsRegistry.addGroup('OAuth', async function () { + await this.add('Accounts_OAuth_Flow_Engine', 'meteor', { + type: 'select', + values: [ + { key: 'meteor', i18nLabel: 'Meteor' }, + { key: 'passport', i18nLabel: 'New flow' }, + ], + public: true, + i18nDescription: 'Accounts_OAuth_Flow_Engine_Description', + }); + await this.section('Drupal', async function () { const enableQuery = { _id: 'Accounts_OAuth_Drupal', diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index 99f7b4b067fb8..63f2110c4ad8c 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -358,6 +358,8 @@ "Accounts_OAuth_Wordpress_server_type_wordpress_com": "Wordpress.com", "Accounts_OAuth_Wordpress_server_type_wp_oauth_server": "WP OAuth Server Plugin", "Accounts_OAuth_Wordpress_token_path": "Token Path", + "Accounts_OAuth_Flow_Engine": "OAuth Authentication Flow", + "Accounts_OAuth_Flow_Engine_Description": "Choose the OAuth implementation. \n **Modern OAuth (Recommended):** Passport-based flow with system browser support on mobile and deep-link callbacks. \n **Legacy Meteor OAuth:** Original Meteor OAuth implementation for backward compatibility.", "Accounts_PasswordReset": "Password Reset", "Accounts_Password_Policy_AtLeastOneLowercase": "At Least One Lowercase", "Accounts_Password_Policy_AtLeastOneLowercase_Description": "Enforce that a password contain at least one lowercase character.", @@ -7253,4 +7255,4 @@ "Avatar_preview_updated": "Avatar preview updated", "Select_message_from_user": "Select message from {{username}}", "Select_message_from_user_with_preview": "Select message from {{username}}: {{message}}" -} \ No newline at end of file +} diff --git a/packages/web-ui-registration/src/LoginServices.tsx b/packages/web-ui-registration/src/LoginServices.tsx index df490dfcd4d59..7a970951a6135 100644 --- a/packages/web-ui-registration/src/LoginServices.tsx +++ b/packages/web-ui-registration/src/LoginServices.tsx @@ -18,6 +18,7 @@ const LoginServices = ({ const { t } = useTranslation(); const services = useLoginServices(); const showFormLogin = useSetting('Accounts_ShowFormLogin'); + const enableNewOAuthFlow = useSetting('Accounts_OAuth_Flow_Engine') === 'passport'; const isDesktopApp = !!window.RocketChatDesktop?.openInBrowser; @@ -52,7 +53,13 @@ const LoginServices = ({ {servicesToShow.length > 0 && ( {servicesToShow.map((service) => ( - + ))} )} diff --git a/packages/web-ui-registration/src/LoginServicesButton.tsx b/packages/web-ui-registration/src/LoginServicesButton.tsx index 62d3d9856cd17..2a4f3700de762 100644 --- a/packages/web-ui-registration/src/LoginServicesButton.tsx +++ b/packages/web-ui-registration/src/LoginServicesButton.tsx @@ -20,17 +20,19 @@ const LoginServicesButton = ({ setError, buttonColor, buttonLabelColor, + enableNewOAuthFlow, ...props }: T & { className?: string; disabled?: boolean; setError?: Dispatch>; + enableNewOAuthFlow?: boolean; }): ReactElement => { const { t } = useTranslation(); const handler = useLoginWithService({ service, buttonLabelText, ...props }); const handleOnClick = useCallback(() => { - if (!servicesSupportedByMeteor.includes(service)) { + if (!servicesSupportedByMeteor.includes(service) && enableNewOAuthFlow) { const url = new URL(window.location.href); const queryParams = url.searchParams; const loginClient = queryParams.get('loginClient'); @@ -51,7 +53,7 @@ const LoginServicesButton = ({ } setError?.([e.error, e.reason]); }); - }, [handler, setError, service]); + }, [handler, setError, service, enableNewOAuthFlow]); return (