From 2243b8ae66d7a5dd148808cf1c09b7629c672728 Mon Sep 17 00:00:00 2001 From: yash-rajpal Date: Fri, 20 Mar 2026 15:35:02 +0530 Subject: [PATCH 01/43] add deprecation warning --- apps/meteor/app/custom-oauth/server/custom_oauth_server.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/meteor/app/custom-oauth/server/custom_oauth_server.js b/apps/meteor/app/custom-oauth/server/custom_oauth_server.js index cba2e7cc035c9..9bc2c686ac331 100644 --- a/apps/meteor/app/custom-oauth/server/custom_oauth_server.js +++ b/apps/meteor/app/custom-oauth/server/custom_oauth_server.js @@ -23,6 +23,9 @@ const logger = new Logger('CustomOAuth'); const Services = {}; const BeforeUpdateOrCreateUserFromExternalService = []; +/** + * @deprecated in favor of new Passport OAuth implementation. + */ export class CustomOAuth { constructor(name, options) { logger.debug({ msg: 'Init CustomOAuth', name, options }); From b0cd754864a740431173d1f380c6c21b6ee1da26 Mon Sep 17 00:00:00 2001 From: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> Date: Fri, 27 Mar 2026 18:55:49 +0530 Subject: [PATCH 02/43] chore: Passport login UI (#39848) --- apps/meteor/client/startup/startup.ts | 9 +++++++-- .../web-ui-registration/src/LoginServicesButton.tsx | 10 +++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/apps/meteor/client/startup/startup.ts b/apps/meteor/client/startup/startup.ts index 45fa6c12b1bbb..30c4a381e9d85 100644 --- a/apps/meteor/client/startup/startup.ts +++ b/apps/meteor/client/startup/startup.ts @@ -12,8 +12,7 @@ import { watchUserId } from '../meteor/user'; Meteor.startup(() => { let status: UserStatus | undefined = undefined; Tracker.autorun(async () => { - const uid = watchUserId(); - if (!uid) { + if (Meteor.loggingOut()) { removeLocalUserData(); return; } @@ -26,6 +25,12 @@ Meteor.startup(() => { return; } + const uid = watchUserId(); + + if (!uid) { + return; + } + const user = await synchronizeUserData(uid); if (!user) { return; diff --git a/packages/web-ui-registration/src/LoginServicesButton.tsx b/packages/web-ui-registration/src/LoginServicesButton.tsx index 7eb22ad5dbfc6..e5488a18ace07 100644 --- a/packages/web-ui-registration/src/LoginServicesButton.tsx +++ b/packages/web-ui-registration/src/LoginServicesButton.tsx @@ -1,4 +1,5 @@ import { Button } from '@rocket.chat/fuselage'; +import { useLocalStorage } from '@rocket.chat/fuselage-hooks'; import type { Keys as IconName } from '@rocket.chat/icons'; import type { LoginService } from '@rocket.chat/ui-contexts'; import { useLoginWithService } from '@rocket.chat/ui-contexts'; @@ -27,14 +28,21 @@ const LoginServicesButton = ({ const { t } = useTranslation(); const handler = useLoginWithService({ service, buttonLabelText, ...props }); + const [isLegacyOAuthEnabled] = useLocalStorage('useLegacyOAuth', false); + const handleOnClick = useCallback(() => { + if (!isLegacyOAuthEnabled) { + window.location.href = `/oauth/${service}`; + return; + } + handler().catch((e: { error?: LoginErrors; reason?: string }) => { if (!e.error || typeof e.error !== 'string') { return; } setError?.([e.error, e.reason]); }); - }, [handler, setError]); + }, [handler, setError, isLegacyOAuthEnabled, service]); return ( + )} ); }; diff --git a/packages/web-ui-registration/src/LoginServicesButton.tsx b/packages/web-ui-registration/src/LoginServicesButton.tsx index e5488a18ace07..dbc9db53d491f 100644 --- a/packages/web-ui-registration/src/LoginServicesButton.tsx +++ b/packages/web-ui-registration/src/LoginServicesButton.tsx @@ -32,7 +32,17 @@ const LoginServicesButton = ({ const handleOnClick = useCallback(() => { if (!isLegacyOAuthEnabled) { - window.location.href = `/oauth/${service}`; + const url = new URL(window.location.href); + const queryParams = url.searchParams; + const loginClient = queryParams.get('loginClient'); + + const redirectUrl = new URL(`/oauth/${service}`, window.location.origin); + + if (loginClient) { + redirectUrl.searchParams.set('loginClient', loginClient); + } + + window.location.href = redirectUrl.toString(); return; } diff --git a/packages/web-ui-registration/tsconfig.build.json b/packages/web-ui-registration/tsconfig.build.json index 73f50b10a9645..78b00e08930a3 100644 --- a/packages/web-ui-registration/tsconfig.build.json +++ b/packages/web-ui-registration/tsconfig.build.json @@ -1,5 +1,5 @@ { "extends": "./tsconfig.json", - "include": ["./src/**/*"], + "include": ["./src/**/*", "./global.d.ts"], "exclude": ["./src/**/*.spec.ts", "./src/**/*.stories.tsx"] } diff --git a/packages/web-ui-registration/tsconfig.json b/packages/web-ui-registration/tsconfig.json index e1348ec0e2e74..46506c90ec74c 100644 --- a/packages/web-ui-registration/tsconfig.json +++ b/packages/web-ui-registration/tsconfig.json @@ -4,5 +4,5 @@ "rootDirs": ["./src","./.storybook"], "outDir": "./dist" }, - "include": ["./src", "./.storybook"], + "include": ["./src", "./.storybook", "./global.d.ts"], } From 9cda3cfbc7f06a6de20e75873b818392791e0a50 Mon Sep 17 00:00:00 2001 From: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> Date: Tue, 19 May 2026 02:44:22 +0530 Subject: [PATCH 17/43] feat: Secret setting for express-sessions secret (#40602) Co-authored-by: Ricardo Garim --- .../server/configuration/configurePassport.ts | 37 +++++++++---------- apps/meteor/server/settings/oauth.ts | 7 ++++ 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/apps/meteor/server/configuration/configurePassport.ts b/apps/meteor/server/configuration/configurePassport.ts index da0469931ca55..d65bf7bb1cd6b 100644 --- a/apps/meteor/server/configuration/configurePassport.ts +++ b/apps/meteor/server/configuration/configurePassport.ts @@ -1,5 +1,4 @@ import type { IUser } from '@rocket.chat/core-typings'; -import { Random } from '@rocket.chat/random'; import express from 'express'; import flash from 'express-flash'; import session from 'express-session'; @@ -13,28 +12,28 @@ import { getOAuthServices } from '../lib/oauth/getOAuthServices'; export const oAuthRouter = express(); -oAuthRouter.use( - session({ - name: 'oauth', - secret: Random.secret(), - resave: false, - saveUninitialized: false, - cookie: { - httpOnly: true, - secure: process.env.NODE_ENV === 'production', - maxAge: 5 * 60 * 1000, // 5 minutes - }, - }), -); - oAuthRouter.enable('trust proxy'); oAuthRouter.set('trust proxy', true); -oAuthRouter.use(passport.initialize()); -oAuthRouter.use(passport.session()); -oAuthRouter.use(flash()); - export const configurePassport = (settings: ICachedSettings) => { + oAuthRouter.use( + session({ + name: 'oauth', + secret: settings.get('Accounts_OAuth_Session_Secret'), + resave: false, + saveUninitialized: false, + cookie: { + httpOnly: true, + secure: process.env.NODE_ENV === 'production', + maxAge: 5 * 60 * 1000, // 5 minutes + }, + }), + ); + + oAuthRouter.use(passport.initialize()); + oAuthRouter.use(passport.session()); + oAuthRouter.use(flash()); + passport.serializeUser((user: any, done) => { done(null, user); }); diff --git a/apps/meteor/server/settings/oauth.ts b/apps/meteor/server/settings/oauth.ts index 2aea9b119cf83..8ee583614882b 100644 --- a/apps/meteor/server/settings/oauth.ts +++ b/apps/meteor/server/settings/oauth.ts @@ -1,3 +1,5 @@ +import { Random } from '@rocket.chat/random'; + import { settingsRegistry } from '../../app/settings/server'; export const createOauthSettings = () => @@ -405,6 +407,11 @@ export const createOauthSettings = () => enableQuery, }); }); + await this.add('Accounts_OAuth_Session_Secret', Random.secret(), { + type: 'string', + secret: true, + hidden: true, + }); return this.section('Proxy', async function () { await this.add('Accounts_OAuth_Proxy_host', 'https://oauth-proxy.rocket.chat', { type: 'string', From 93af0a874ccee6cba7d74478f03b5e9bed5d3d52 Mon Sep 17 00:00:00 2001 From: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> Date: Tue, 19 May 2026 02:59:43 +0530 Subject: [PATCH 18/43] chore: Passport Nextcloud OAuth (#40591) --- .../app/custom-oauth/server/customOAuth.ts | 2 + apps/meteor/app/nextcloud/server/lib.ts | 45 +++++++++++-------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/apps/meteor/app/custom-oauth/server/customOAuth.ts b/apps/meteor/app/custom-oauth/server/customOAuth.ts index 04b940e7d95b3..21afcf71ece4a 100644 --- a/apps/meteor/app/custom-oauth/server/customOAuth.ts +++ b/apps/meteor/app/custom-oauth/server/customOAuth.ts @@ -236,6 +236,8 @@ export class CustomOAuthStrategy extends Strategy { try { const result = JSON.parse(typeof body === 'string' ? body : body.toString()); const normalizedIdentity = this.normalizeIdentity(result); + //Nextcloud URL needed on addWebdavServer + normalizedIdentity.serverURL = this.serverURL; return done(null, normalizedIdentity); } catch (e) { return done(new Error(`Failed to parse identity from ${this.name} at ${this.identityPath}. ${e}`)); diff --git a/apps/meteor/app/nextcloud/server/lib.ts b/apps/meteor/app/nextcloud/server/lib.ts index 28cf52da57a17..fc2b592b7c1ad 100644 --- a/apps/meteor/app/nextcloud/server/lib.ts +++ b/apps/meteor/app/nextcloud/server/lib.ts @@ -1,14 +1,12 @@ -import type { OauthConfig } from '@rocket.chat/core-typings'; +import type { OAuthConfiguration } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; -import _ from 'underscore'; -import { CustomOAuth } from '../../custom-oauth/server/custom_oauth_server'; -import { settings } from '../../settings/server'; +import { addPassportCustomOAuth } from '../../../server/lib/oauth/addPassportCustomOAuth'; +import { settings } from '../../settings/server/cached'; -const config: OauthConfig = { - serverURL: '', +const NEXTCLOUD_PATHS = { tokenPath: '/index.php/apps/oauth2/api/v1/token', - tokenSentVia: 'header', + tokenSentVia: 'header' as OAuthConfiguration['tokenSentVia'], authorizePath: '/index.php/apps/oauth2/authorize', identityPath: '/ocs/v2.php/cloud/user?format=json', scope: 'openid', @@ -18,20 +16,31 @@ const config: OauthConfig = { }, }; -const Nextcloud = new CustomOAuth('nextcloud', config); +function configureNextcloudOAuth(): void { + const enabled = settings.get('Accounts_OAuth_Nextcloud'); + if (!enabled) { + return; + } -const fillServerURL = _.debounce((): void => { - const nextcloudURL = settings.get('Accounts_OAuth_Nextcloud_URL'); - if (!nextcloudURL) { - if (nextcloudURL === undefined) { - return fillServerURL(); - } + const serverURL = settings.get('Accounts_OAuth_Nextcloud_URL')?.trim().replace(/\/*$/, ''); + const clientId = settings.get('Accounts_OAuth_Nextcloud_id'); + const clientSecret = settings.get('Accounts_OAuth_Nextcloud_secret'); + + if (!serverURL || !clientId || !clientSecret) { return; } - config.serverURL = nextcloudURL.trim().replace(/\/*$/, ''); - return Nextcloud.configure(config); -}, 1000); + + addPassportCustomOAuth('nextcloud', { + ...NEXTCLOUD_PATHS, + serverURL, + clientId, + clientSecret, + }); +} Meteor.startup(() => { - settings.watch('Accounts_OAuth_Nextcloud_URL', () => fillServerURL()); + settings.watchMultiple( + ['Accounts_OAuth_Nextcloud', 'Accounts_OAuth_Nextcloud_URL', 'Accounts_OAuth_Nextcloud_id', 'Accounts_OAuth_Nextcloud_secret'], + configureNextcloudOAuth, + ); }); From c7f7306a729df7c58bf9a76d8dbb7646b5bbfd99 Mon Sep 17 00:00:00 2001 From: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> Date: Tue, 19 May 2026 03:14:11 +0530 Subject: [PATCH 19/43] chore: Passport Meteor OAuth (#40595) --- .../app/meteor-developer/server/index.ts | 1 + .../meteor/app/meteor-developer/server/lib.ts | 46 +++++++++++++++++++ apps/meteor/server/importPackages.ts | 1 + 3 files changed, 48 insertions(+) create mode 100644 apps/meteor/app/meteor-developer/server/index.ts create mode 100644 apps/meteor/app/meteor-developer/server/lib.ts diff --git a/apps/meteor/app/meteor-developer/server/index.ts b/apps/meteor/app/meteor-developer/server/index.ts new file mode 100644 index 0000000000000..cf327e4971bb2 --- /dev/null +++ b/apps/meteor/app/meteor-developer/server/index.ts @@ -0,0 +1 @@ +import './lib'; diff --git a/apps/meteor/app/meteor-developer/server/lib.ts b/apps/meteor/app/meteor-developer/server/lib.ts new file mode 100644 index 0000000000000..cb532732ead92 --- /dev/null +++ b/apps/meteor/app/meteor-developer/server/lib.ts @@ -0,0 +1,46 @@ +import type { OAuthConfiguration } from '@rocket.chat/core-typings'; +import { Meteor } from 'meteor/meteor'; +import passport from 'passport'; + +import { addPassportCustomOAuth } from '../../../server/lib/oauth/addPassportCustomOAuth'; +import { settings } from '../../settings/server'; + +const config: Partial = { + serverURL: 'https://www.meteor.com', + authorizePath: '/oauth2/authorize', + tokenPath: '/oauth2/token', + identityPath: '/api/v1/identity', + scope: 'email', + tokenSentVia: 'header', + addAutopublishFields: { + forLoggedInUser: ['services.meteor-developer'], + forOtherUsers: ['services.meteor-developer.username'], + }, +}; + +const serviceKey = 'meteor-developer'; + +const configureMeteorDeveloperOAuth = (): void => { + passport.unuse(serviceKey); + + const enabled = settings.get('Accounts_OAuth_Meteor'); + if (!enabled) { + return; + } + + const clientId = settings.get('Accounts_OAuth_Meteor_id'); + const clientSecret = settings.get('Accounts_OAuth_Meteor_secret'); + + if (!clientId || !clientSecret) { + return; + } + + addPassportCustomOAuth(serviceKey, { ...config, clientId, clientSecret }); +}; + +Meteor.startup(() => { + settings.watchMultiple( + ['Accounts_OAuth_Meteor', 'Accounts_OAuth_Meteor_id', 'Accounts_OAuth_Meteor_secret'], + configureMeteorDeveloperOAuth, + ); +}); diff --git a/apps/meteor/server/importPackages.ts b/apps/meteor/server/importPackages.ts index 8dd1046b515b9..228d809fe6dc2 100644 --- a/apps/meteor/server/importPackages.ts +++ b/apps/meteor/server/importPackages.ts @@ -33,6 +33,7 @@ import '../app/importer-slack-users/server'; import '../app/integrations/server'; import '../app/irc/server'; import '../app/lib/server'; +import '../app/meteor-developer/server'; import '../app/linkedin/server'; import '../app/token-login/server'; import '../app/mailer/server/api'; From 80a082bcf60bffdd9f08bb93a871c80b86833ebf Mon Sep 17 00:00:00 2001 From: yash-rajpal Date: Tue, 19 May 2026 03:17:31 +0530 Subject: [PATCH 20/43] me stupid --- apps/meteor/client/lib/sdk/ddpSdk.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/meteor/client/lib/sdk/ddpSdk.ts b/apps/meteor/client/lib/sdk/ddpSdk.ts index c671880cf3fe6..09c2b43ac10a7 100644 --- a/apps/meteor/client/lib/sdk/ddpSdk.ts +++ b/apps/meteor/client/lib/sdk/ddpSdk.ts @@ -68,8 +68,7 @@ export const getDdpSdk = (): DDPSDK => { return instance; }; -export const readStoredLoginToken = (): string | null => - typeof window !== 'undefined' ? window.localStorage.getItem('Meteor.loginToken') : null; +export const readStoredLoginToken = (): string | null => getStoredItem(STORAGE_KEYS.LOGIN_TOKEN); let inflightLogin: Promise | undefined; From afda09bd58d280c104dc0d3635fe9d1f793c182d Mon Sep 17 00:00:00 2001 From: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> Date: Tue, 19 May 2026 18:37:26 +0530 Subject: [PATCH 21/43] chore: Passport Github Enterprise OAuth (#40608) --- .../server/lib/oauth/configureOAuthServices.ts | 1 + .../server/lib/oauth/createOAuthServiceConfig.ts | 13 +++++++++++++ apps/meteor/server/lib/oauth/oauthConfigs.ts | 4 ++++ 3 files changed, 18 insertions(+) diff --git a/apps/meteor/server/lib/oauth/configureOAuthServices.ts b/apps/meteor/server/lib/oauth/configureOAuthServices.ts index 6fae927444167..a49df2f0d4529 100644 --- a/apps/meteor/server/lib/oauth/configureOAuthServices.ts +++ b/apps/meteor/server/lib/oauth/configureOAuthServices.ts @@ -20,6 +20,7 @@ export const configureOAuthServices = (oauthServiceConfig: OAuthServiceConfig[], config.provider, new Strategy( { + ...config, clientID: config.clientId, clientSecret: config.clientSecret, callbackURL: `${siteUrl}/oauth/${config.provider}/callback`, diff --git a/apps/meteor/server/lib/oauth/createOAuthServiceConfig.ts b/apps/meteor/server/lib/oauth/createOAuthServiceConfig.ts index 723f1db7a665b..2657e6aba3c72 100644 --- a/apps/meteor/server/lib/oauth/createOAuthServiceConfig.ts +++ b/apps/meteor/server/lib/oauth/createOAuthServiceConfig.ts @@ -14,6 +14,19 @@ export type OAuthServiceConfig = { export const createOAuthServiceConfig = (settings: ICachedSettings, services: string[]): OAuthServiceConfig[] => { return services.map((service) => { + if (service === 'github_enterprise') { + return { + provider: service, + clientId: settings.get('Accounts_OAuth_GitHub_Enterprise_id'), + clientSecret: settings.get('Accounts_OAuth_GitHub_Enterprise_secret'), + authorizationURL: `${settings.get('API_GitHub_Enterprise_URL')}/login/oauth/authorize`, + tokenURL: `${settings.get('API_GitHub_Enterprise_URL')}/login/oauth/access_token`, + userProfileURL: `${settings.get('API_GitHub_Enterprise_URL')}/api/v3/user`, + strategy: OAuthConfigs.github_enterprise.strategy, + scope: OAuthConfigs.github_enterprise.scope, + }; + } + return { provider: service, strategy: OAuthConfigs[service].strategy, diff --git a/apps/meteor/server/lib/oauth/oauthConfigs.ts b/apps/meteor/server/lib/oauth/oauthConfigs.ts index 55b2c607cbdcd..4ddd63333b3ba 100644 --- a/apps/meteor/server/lib/oauth/oauthConfigs.ts +++ b/apps/meteor/server/lib/oauth/oauthConfigs.ts @@ -21,6 +21,10 @@ export const OAuthConfigs: Record = { strategy: GoogleStrategy, scope: ['email', 'profile'], }, + github_enterprise: { + strategy: GitHubStrategy, + scope: ['user:email'], + }, } as const; export type Provider = keyof typeof OAuthConfigs; From dc44d7c4fc8b54a36f6c4b90945bc1e9fae30197 Mon Sep 17 00:00:00 2001 From: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> Date: Tue, 19 May 2026 20:38:18 +0530 Subject: [PATCH 22/43] chore: Passport Gitlab OAuth (#40609) --- apps/meteor/app/gitlab/server/lib.ts | 49 +++++++++++++++++++++------- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/apps/meteor/app/gitlab/server/lib.ts b/apps/meteor/app/gitlab/server/lib.ts index 0f4d330cf5970..3df1c1ded5496 100644 --- a/apps/meteor/app/gitlab/server/lib.ts +++ b/apps/meteor/app/gitlab/server/lib.ts @@ -1,11 +1,12 @@ -import type { OauthConfig } from '@rocket.chat/core-typings'; +import type { OAuthConfiguration } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; +import passport from 'passport'; import _ from 'underscore'; -import { CustomOAuth } from '../../custom-oauth/server/custom_oauth_server'; +import { addPassportCustomOAuth } from '../../../server/lib/oauth/addPassportCustomOAuth'; import { settings } from '../../settings/server'; -const config: OauthConfig = { +const config: Partial = { serverURL: 'https://gitlab.com', identityPath: '/api/v4/user', scope: 'read_user', @@ -17,15 +18,39 @@ const config: OauthConfig = { accessTokenParam: 'access_token', }; -const Gitlab = new CustomOAuth('gitlab', config); +const configureGitlabOAuth = () => { + passport.unuse('gitlab'); + + const enabled = settings.get('Accounts_OAuth_Gitlab'); + if (!enabled) { + return; + } + + const clientId = settings.get('Accounts_OAuth_Gitlab_id'); + const clientSecret = settings.get('Accounts_OAuth_Gitlab_secret'); + const serverURL = settings.get('API_Gitlab_URL').trim().replace(/\/*$/, '') || config.serverURL; + const identityPath = settings.get('Accounts_OAuth_Gitlab_identity_path') || config.identityPath; + const mergeUsers = Boolean(settings.get('Accounts_OAuth_Gitlab_merge_users')); + + if (!clientId || !clientSecret) { + return; + } + + addPassportCustomOAuth('gitlab', { ...config, clientId, clientSecret, serverURL, identityPath, mergeUsers }); +}; Meteor.startup(() => { - const updateConfig = _.debounce(() => { - config.serverURL = settings.get('API_Gitlab_URL').trim().replace(/\/*$/, '') || config.serverURL; - config.identityPath = settings.get('Accounts_OAuth_Gitlab_identity_path') || config.identityPath; - config.mergeUsers = Boolean(settings.get('Accounts_OAuth_Gitlab_merge_users')); - Gitlab.configure(config); - }, 300); - - settings.watchMultiple(['API_Gitlab_URL', 'Accounts_OAuth_Gitlab_identity_path', 'Accounts_OAuth_Gitlab_merge_users'], updateConfig); + const updateConfig = _.debounce(configureGitlabOAuth, 300); + + settings.watchMultiple( + [ + 'Accounts_OAuth_Gitlab', + 'API_Gitlab_URL', + 'Accounts_OAuth_Gitlab_id', + 'Accounts_OAuth_Gitlab_secret', + 'Accounts_OAuth_Gitlab_identity_path', + 'Accounts_OAuth_Gitlab_merge_users', + ], + updateConfig, + ); }); From f052ca893663fa49b8da6fee9d530ba6772d86bc Mon Sep 17 00:00:00 2001 From: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> Date: Tue, 19 May 2026 20:55:05 +0530 Subject: [PATCH 23/43] chore: Passport Dolphin (#40611) --- apps/meteor/app/dolphin/server/lib.ts | 39 ++++++++++++++++++++------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/apps/meteor/app/dolphin/server/lib.ts b/apps/meteor/app/dolphin/server/lib.ts index 826a277b81e4e..77622fe43edab 100644 --- a/apps/meteor/app/dolphin/server/lib.ts +++ b/apps/meteor/app/dolphin/server/lib.ts @@ -1,13 +1,15 @@ -import type { IUser } from '@rocket.chat/core-typings'; +import type { IUser, OAuthConfiguration } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; import { ServiceConfiguration } from 'meteor/service-configuration'; +import passport from 'passport'; +import _ from 'underscore'; import { callbacks } from '../../../server/lib/callbacks'; import { beforeCreateUserCallback } from '../../../server/lib/callbacks/beforeCreateUserCallback'; -import { CustomOAuth } from '../../custom-oauth/server/custom_oauth_server'; +import { addPassportCustomOAuth } from '../../../server/lib/oauth/addPassportCustomOAuth'; import { settings } from '../../settings/server'; -const config = { +const config: Partial = { serverURL: '', authorizePath: '/m/oauth2/auth/', tokenPath: '/m/oauth2/token/', @@ -20,8 +22,6 @@ const config = { accessTokenParam: 'access_token', }; -const Dolphin = new CustomOAuth('dolphin', config); - function DolphinOnCreateUser(options: any, user?: IUser) { // TODO: callbacks Fix this if (user?.services?.dolphin?.NickName) { @@ -30,11 +30,32 @@ function DolphinOnCreateUser(options: any, user?: IUser) { return options; } +const configureDolphinOAuth = () => { + passport.unuse('dolphin'); + + const enabled = settings.get('Accounts_OAuth_Dolphin'); + if (!enabled) { + return; + } + + const serverURL = settings.get('Accounts_OAuth_Dolphin_URL').trim().replace(/\/*$/, ''); + const clientId = settings.get('Accounts_OAuth_Dolphin_id'); + const clientSecret = settings.get('Accounts_OAuth_Dolphin_secret'); + + if (!clientId || !clientSecret || !serverURL) { + return; + } + + addPassportCustomOAuth('dolphin', { ...config, serverURL, clientId, clientSecret }); +}; + Meteor.startup(async () => { - settings.watch('Accounts_OAuth_Dolphin_URL', (value) => { - config.serverURL = value; - return Dolphin.configure(config); - }); + const updateConfig = () => _.debounce(configureDolphinOAuth, 300); + + settings.watchMultiple( + ['Accounts_OAuth_Dolphin', 'Accounts_OAuth_Dolphin_URL', 'Accounts_OAuth_Dolphin_id', 'Accounts_OAuth_Dolphin_secret'], + updateConfig, + ); if (settings.get('Accounts_OAuth_Dolphin_URL')) { const data = { From 02fa86bd1a12854c66d612806ac676cdcf0ed2d4 Mon Sep 17 00:00:00 2001 From: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> Date: Tue, 19 May 2026 20:55:32 +0530 Subject: [PATCH 24/43] chore: Passport Drupal OAuth (#40610) --- apps/meteor/app/drupal/server/lib.ts | 40 +++++++++++++++++++--------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/apps/meteor/app/drupal/server/lib.ts b/apps/meteor/app/drupal/server/lib.ts index d137551fb8377..a4f71bd81696f 100644 --- a/apps/meteor/app/drupal/server/lib.ts +++ b/apps/meteor/app/drupal/server/lib.ts @@ -1,14 +1,12 @@ -import type { OauthConfig } from '@rocket.chat/core-typings'; +import type { OAuthConfiguration } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; +import passport from 'passport'; +import _ from 'underscore'; -import { CustomOAuth } from '../../custom-oauth/server/custom_oauth_server'; +import { addPassportCustomOAuth } from '../../../server/lib/oauth/addPassportCustomOAuth'; import { settings } from '../../settings/server'; -// Drupal Server CallBack URL needs to be http(s)://{rocketchat.server}[:port]/_oauth/drupal -// In RocketChat -> Administration the URL needs to be http(s)://{drupal.server}/ - -const config: OauthConfig = { - serverURL: '', +const config: Partial = { identityPath: '/oauth2/UserInfo', authorizePath: '/oauth2/authorize', tokenPath: '/oauth2/token', @@ -23,11 +21,29 @@ const config: OauthConfig = { accessTokenParam: 'access_token', }; -const Drupal = new CustomOAuth('drupal', config); +const configureDrupalOAuth = () => { + passport.unuse('drupal'); + const enabled = settings.get('Accounts_OAuth_Drupal'); + if (!enabled) { + return; + } + + const serverURL = settings.get('API_Drupal_URL').trim().replace(/\/*$/, ''); + const clientId = settings.get('Accounts_OAuth_Drupal_id'); + const clientSecret = settings.get('Accounts_OAuth_Drupal_secret'); + + if (!clientId || !clientSecret || !serverURL) { + return; + } + + addPassportCustomOAuth('drupal', { ...config, serverURL, clientId, clientSecret }); +}; Meteor.startup(() => { - settings.watch('API_Drupal_URL', (value) => { - config.serverURL = value; - Drupal.configure(config); - }); + const updateConfig = _.debounce(configureDrupalOAuth, 300); + + settings.watchMultiple( + ['Accounts_OAuth_Drupal', 'API_Drupal_URL', 'Accounts_OAuth_Drupal_id', 'Accounts_OAuth_Drupal_secret'], + updateConfig, + ); }); From f7090645e263a9c0188f597b24cc03bab4f9dd16 Mon Sep 17 00:00:00 2001 From: Yash Rajpal <58601732+yash-rajpal@users.noreply.github.com> Date: Tue, 19 May 2026 21:09:37 +0530 Subject: [PATCH 25/43] chore: Passport Wordpress OAuth (#40593) --- apps/meteor/app/wordpress/server/lib.ts | 26 +++++++++++-------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/apps/meteor/app/wordpress/server/lib.ts b/apps/meteor/app/wordpress/server/lib.ts index 7777ea1382215..eb1ce2c01ceb2 100644 --- a/apps/meteor/app/wordpress/server/lib.ts +++ b/apps/meteor/app/wordpress/server/lib.ts @@ -1,12 +1,13 @@ -import type { OauthConfig } from '@rocket.chat/core-typings'; +import type { OAuthConfiguration } from '@rocket.chat/core-typings'; import { Meteor } from 'meteor/meteor'; import { ServiceConfiguration } from 'meteor/service-configuration'; +import passport from 'passport'; import _ from 'underscore'; -import { CustomOAuth } from '../../custom-oauth/server/custom_oauth_server'; +import { addPassportCustomOAuth } from '../../../server/lib/oauth/addPassportCustomOAuth'; import { settings } from '../../settings/server'; -const config: OauthConfig = { +const config: Partial = { serverURL: '', identityPath: '/oauth/me', @@ -17,7 +18,7 @@ const config: OauthConfig = { accessTokenParam: 'access_token', }; -const WordPress = new CustomOAuth('wordpress', config); +const serviceKey = 'wordpress'; const fillSettings = _.debounce(async (): Promise => { config.serverURL = settings.get('API_Wordpress_URL'); @@ -28,11 +29,7 @@ const fillSettings = _.debounce(async (): Promise => { return; } - delete config.identityPath; - delete config.identityTokenSentVia; - delete config.authorizePath; - delete config.tokenPath; - delete config.scope; + passport.unuse(serviceKey); const serverType = settings.get('Accounts_OAuth_Wordpress_server_type'); switch (serverType) { @@ -59,7 +56,7 @@ const fillSettings = _.debounce(async (): Promise => { break; case 'wordpress-com': config.identityPath = 'https://public-api.wordpress.com/rest/v1/me'; - config.identityTokenSentVia = 'header'; + config.identityTokenSentVia = 'header' as OAuthConfiguration['identityTokenSentVia']; config.authorizePath = 'https://public-api.wordpress.com/oauth2/authorize'; config.tokenPath = 'https://public-api.wordpress.com/oauth2/token'; config.scope = 'auth'; @@ -69,12 +66,13 @@ const fillSettings = _.debounce(async (): Promise => { break; } - const result = WordPress.configure(config); + addPassportCustomOAuth(serviceKey, config); + const enabled = settings.get('Accounts_OAuth_Wordpress'); if (enabled) { await ServiceConfiguration.configurations.upsertAsync( { - service: 'wordpress', + service: serviceKey, }, { $set: config, @@ -82,11 +80,9 @@ const fillSettings = _.debounce(async (): Promise => { ); } else { await ServiceConfiguration.configurations.removeAsync({ - service: 'wordpress', + service: serviceKey, }); } - - return result; }, 1000); Meteor.startup(() => { From 9509e9b6bb0dc4b71861a5358b6b1b994c6fce9f Mon Sep 17 00:00:00 2001 From: yash-rajpal Date: Tue, 19 May 2026 21:38:22 +0530 Subject: [PATCH 26/43] add rate limiter --- .../server/configuration/configurePassport.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/apps/meteor/server/configuration/configurePassport.ts b/apps/meteor/server/configuration/configurePassport.ts index d65bf7bb1cd6b..2109523459176 100644 --- a/apps/meteor/server/configuration/configurePassport.ts +++ b/apps/meteor/server/configuration/configurePassport.ts @@ -1,6 +1,7 @@ import type { IUser } from '@rocket.chat/core-typings'; import express from 'express'; import flash from 'express-flash'; +import rateLimit from 'express-rate-limit'; import session from 'express-session'; import { WebApp } from 'meteor/webapp'; import passport from 'passport'; @@ -34,6 +35,22 @@ export const configurePassport = (settings: ICachedSettings) => { oAuthRouter.use(passport.session()); oAuthRouter.use(flash()); + const oauthRateLimiter = rateLimit({ + windowMs: settings.get('API_Enable_Rate_Limiter_Limit_Time_Default'), + max: settings.get('API_Enable_Rate_Limiter_Limit_Calls_Default'), + skip: () => + settings.get('API_Enable_Rate_Limiter') !== true || + (process.env.NODE_ENV === 'development' && settings.get('API_Enable_Rate_Limiter_Dev') !== true), + handler: (_req, res) => { + res.status(429).json({ + success: false, + error: 'Too many requests. Please try again later.', + }); + }, + }); + + oAuthRouter.use(oauthRateLimiter); + passport.serializeUser((user: any, done) => { done(null, user); }); From d394e16776bd5fe1d171f6692c0f5e0934adec05 Mon Sep 17 00:00:00 2001 From: yash-rajpal Date: Tue, 19 May 2026 22:26:06 +0530 Subject: [PATCH 27/43] exclude saml, cas, ldap from login flow --- .../web-ui-registration/src/LoginServices.tsx | 21 +++++++++++++------ .../src/LoginServicesButton.tsx | 9 ++++---- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/packages/web-ui-registration/src/LoginServices.tsx b/packages/web-ui-registration/src/LoginServices.tsx index b89b866fbabf4..5e9d3972e60ea 100644 --- a/packages/web-ui-registration/src/LoginServices.tsx +++ b/packages/web-ui-registration/src/LoginServices.tsx @@ -1,11 +1,13 @@ import { Button, ButtonGroup, Divider } from '@rocket.chat/fuselage'; import { useLoginServices, useSetting } from '@rocket.chat/ui-contexts'; -import type { Dispatch, ReactElement, SetStateAction } from 'react'; +import { useMemo, type Dispatch, type ReactElement, type SetStateAction } from 'react'; import { useTranslation } from 'react-i18next'; import type { LoginErrorState } from './LoginForm'; import LoginServicesButton from './LoginServicesButton'; +const servicesToBeShownOnDesktop = ['saml', 'cas', 'ldap']; + const LoginServices = ({ disabled, setError, @@ -17,12 +19,17 @@ const LoginServices = ({ const services = useLoginServices(); const showFormLogin = useSetting('Accounts_ShowFormLogin'); + const isDesktopApp = !!window.RocketChatDesktop?.openInBrowser; + + const servicesToShow = useMemo( + () => (isDesktopApp ? services.filter(({ service }) => servicesToBeShownOnDesktop.includes(service)) : services), + [isDesktopApp, services], + ); + if (services.length === 0) { return null; } - const isDesktopApp = !!window.RocketChatDesktop?.openInBrowser; - const handleLoginOnWeb = () => { if (!isDesktopApp) { return; @@ -38,15 +45,17 @@ const LoginServices = ({ {t('registration.component.form.divider')} )} - {!isDesktopApp && ( + + {servicesToShow.length > 0 && ( - {services.map((service) => ( + {servicesToShow.map((service) => ( ))} )} + {isDesktopApp && ( - )} diff --git a/packages/web-ui-registration/src/LoginServicesButton.tsx b/packages/web-ui-registration/src/LoginServicesButton.tsx index dbc9db53d491f..62d3d9856cd17 100644 --- a/packages/web-ui-registration/src/LoginServicesButton.tsx +++ b/packages/web-ui-registration/src/LoginServicesButton.tsx @@ -1,5 +1,4 @@ import { Button } from '@rocket.chat/fuselage'; -import { useLocalStorage } from '@rocket.chat/fuselage-hooks'; import type { Keys as IconName } from '@rocket.chat/icons'; import type { LoginService } from '@rocket.chat/ui-contexts'; import { useLoginWithService } from '@rocket.chat/ui-contexts'; @@ -9,6 +8,8 @@ import { useTranslation } from 'react-i18next'; import type { LoginErrorState, LoginErrors } from './LoginForm'; +const servicesSupportedByMeteor = ['saml', 'cas', 'ldap']; + const LoginServicesButton = ({ buttonLabelText, icon, @@ -28,10 +29,8 @@ const LoginServicesButton = ({ const { t } = useTranslation(); const handler = useLoginWithService({ service, buttonLabelText, ...props }); - const [isLegacyOAuthEnabled] = useLocalStorage('useLegacyOAuth', false); - const handleOnClick = useCallback(() => { - if (!isLegacyOAuthEnabled) { + if (!servicesSupportedByMeteor.includes(service)) { const url = new URL(window.location.href); const queryParams = url.searchParams; const loginClient = queryParams.get('loginClient'); @@ -52,7 +51,7 @@ const LoginServicesButton = ({ } setError?.([e.error, e.reason]); }); - }, [handler, setError, isLegacyOAuthEnabled, service]); + }, [handler, setError, service]); return (