Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/shy-dolls-protect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@rocket.chat/meteor': patch
---

Fixes an issue where OAuth login buttons were not showing up on the login page
2 changes: 1 addition & 1 deletion apps/meteor/app/apple/client/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CustomOAuth } from '../../custom-oauth/client/CustomOAuth';
import { config } from '../lib/config';

new CustomOAuth('apple', config);
CustomOAuth.configureOAuthService('apple', config);
30 changes: 30 additions & 0 deletions apps/meteor/app/custom-oauth/client/CustomOAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import { isURL } from '../../../lib/utils/isURL';
// completion. Takes one argument, credentialToken on success, or Error on
// error.

const configuredOAuthServices = new Map<string, CustomOAuth>();

export class CustomOAuth implements IOAuthProvider {
public serverURL: string;

Expand Down Expand Up @@ -122,4 +124,32 @@ export class CustomOAuth implements IOAuthProvider {
},
});
}

static configureOAuthService(serviceName: string, options: OauthConfig): CustomOAuth {
const existingInstance = configuredOAuthServices.get(serviceName);
if (existingInstance) {
existingInstance.configure(options);
return existingInstance;
}

// If we don't have a reference to the instance for this service and it was already registered on meteor,
// then there's nothing we can do to update it
if (Accounts.oauth.serviceNames().includes(serviceName)) {
throw new Error(`CustomOAuth service [${serviceName}] already registered, skipping new configuration.`);
}

const instance = new CustomOAuth(serviceName, options);
configuredOAuthServices.set(serviceName, instance);
return instance;
}

static configureCustomOAuthService(serviceName: string, options: OauthConfig): CustomOAuth | undefined {
// Custom OAuth services are configured based on the login service list, so if this ends up being called multiple times, simply ignore it
// Non-Custom OAuth services are configured based on code, so if configureOAuthService is called multiple times for them, it's a bug and it should throw.
try {
return this.configureOAuthService(serviceName, options);
} catch (e) {
console.error(e);
}
}
}
2 changes: 1 addition & 1 deletion apps/meteor/app/dolphin/client/hooks/useDolphin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const config = {
accessTokenParam: 'access_token',
};

const Dolphin = new CustomOAuth('dolphin', config);
const Dolphin = CustomOAuth.configureOAuthService('dolphin', config);

export const useDolphin = () => {
const enabled = useSetting('Accounts_OAuth_Dolphin');
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/app/drupal/client/hooks/useDrupal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const config: OauthConfig = {
accessTokenParam: 'access_token',
};

const Drupal = new CustomOAuth('drupal', config);
const Drupal = CustomOAuth.configureOAuthService('drupal', config);

export const useDrupal = () => {
const drupalUrl = useSetting('API_Drupal_URL') as string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const config: OauthConfig = {
},
};

const GitHubEnterprise = new CustomOAuth('github_enterprise', config);
const GitHubEnterprise = CustomOAuth.configureOAuthService('github_enterprise', config);

export const useGitHubEnterpriseAuth = () => {
const githubApiUrl = useSetting('API_GitHub_Enterprise_URL') as string;
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/app/gitlab/client/hooks/useGitLabAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const config: OauthConfig = {
accessTokenParam: 'access_token',
};

const Gitlab = new CustomOAuth('gitlab', config);
const Gitlab = CustomOAuth.configureOAuthService('gitlab', config);

export const useGitLabAuth = () => {
const gitlabApiUrl = useSetting('API_Gitlab_URL') as string;
Expand Down
2 changes: 1 addition & 1 deletion apps/meteor/app/nextcloud/client/useNextcloud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const config: OauthConfig = {
},
};

const Nextcloud = new CustomOAuth('nextcloud', config);
const Nextcloud = CustomOAuth.configureOAuthService('nextcloud', config);

export const useNextcloud = (): void => {
const nextcloudURL = useSetting('Accounts_OAuth_Nextcloud_URL') as string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const config: OauthConfig = {
accessTokenParam: 'access_token',
};

const Tokenpass = new CustomOAuth('tokenpass', config);
const Tokenpass = CustomOAuth.configureOAuthService('tokenpass', config);

export const useTokenPassAuth = () => {
const setting = useSetting('API_Tokenpass_URL') as string | undefined;
Expand Down
12 changes: 9 additions & 3 deletions apps/meteor/client/lib/loginServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,11 @@ class LoginServices extends Emitter<LoginServicesEvents> {

if (state === 'loaded') {
this.retries = 0;
this.emit('loaded', services);
try {
this.emit('loaded', services);
} catch (e) {
console.error('Failed to apply loaded listed of login services.', e);
}
}
}

Expand Down Expand Up @@ -113,13 +117,15 @@ class LoginServices extends Emitter<LoginServicesEvents> {
return this.serviceButtons;
}

public onLoad(callback: (services: LoginServiceConfiguration[]) => void) {
public onLoad(callback: (services: LoginServiceConfiguration[]) => void): () => void {
if (this.ready) {
return callback(this.services);
callback(this.services);
return () => undefined;
}

void this.loadServices();
this.once('loaded', callback);
return () => this.off('loaded', callback);
}

public async loadServices(): Promise<void> {
Expand Down
30 changes: 16 additions & 14 deletions apps/meteor/client/sidebar/hooks/useCustomOAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,21 @@ import { CustomOAuth } from '../../../app/custom-oauth/client/CustomOAuth';
import { loginServices } from '../../lib/loginServices';

export const useCustomOAuth = () => {
useEffect(() => {
loginServices.onLoad((services) => {
for (const service of services) {
if (!('custom' in service && service.custom)) {
continue;
}
useEffect(
() =>
loginServices.onLoad((services) => {
for (const service of services) {
if (!('custom' in service && service.custom)) {
continue;
}

new CustomOAuth(service.service, {
serverURL: service.serverURL,
authorizePath: service.authorizePath,
scope: service.scope,
});
}
});
}, []);
CustomOAuth.configureCustomOAuthService(service.service, {
serverURL: service.serverURL,
authorizePath: service.authorizePath,
scope: service.scope,
});
}
}),
[],
);
};
2 changes: 1 addition & 1 deletion apps/meteor/client/views/root/hooks/useWordPressOAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const configDefault: OauthConfig = {
accessTokenParam: 'access_token',
};

const WordPress = new CustomOAuth('wordpress', configDefault);
const WordPress = CustomOAuth.configureOAuthService('wordpress', configDefault);

const configureServerType = (
serverType: string,
Expand Down
2 changes: 2 additions & 0 deletions apps/meteor/definition/externals/meteor/accounts-base.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ declare module 'meteor/accounts-base' {
): (credentialTokenOrError?: string | globalThis.Error | Meteor.Error | Meteor.TypedError) => void;

function registerService(name: string): void;

function serviceNames(): string[];
}
}
}
Loading