RocketChat/Rocket.Chat

View on GitHub
apps/meteor/client/meteorOverrides/login/google.ts

Summary

Maintainability
A
0 mins
Test Coverage
import { Random } from '@rocket.chat/random';
import { Accounts } from 'meteor/accounts-base';
import { Google } from 'meteor/google-oauth';
import { Meteor } from 'meteor/meteor';
import { OAuth } from 'meteor/oauth';

import { overrideLoginMethod, type LoginCallback } from '../../lib/2fa/overrideLoginMethod';
import { wrapRequestCredentialFn } from '../../lib/wrapRequestCredentialFn';
import { createOAuthTotpLoginMethod } from './oauth';

declare module 'meteor/accounts-base' {
    // eslint-disable-next-line @typescript-eslint/no-namespace
    namespace Accounts {
        export const _options: {
            restrictCreationByEmailDomain?: string | (() => string);
            forbidClientAccountCreation?: boolean | undefined;
        };
    }
}

declare module 'meteor/meteor' {
    // eslint-disable-next-line @typescript-eslint/no-namespace
    namespace Meteor {
        function loginWithGoogle(
            options?:
                | Meteor.LoginWithExternalServiceOptions & {
                        loginUrlParameters?: {
                            include_granted_scopes?: boolean;
                            hd?: string;
                        };
                  },
            callback?: LoginCallback,
        ): void;
    }
}

const { loginWithGoogle } = Meteor;

const innerLoginWithGoogleAndTOTP = createOAuthTotpLoginMethod(Google);

const loginWithGoogleAndTOTP = (
    options:
        | (Meteor.LoginWithExternalServiceOptions & {
                loginUrlParameters?: {
                    include_granted_scopes?: boolean;
                    hd?: string;
                };
          })
        | undefined,
    code: string,
    callback?: LoginCallback,
) => {
    if (Meteor.isCordova && Google.signIn) {
        // After 20 April 2017, Google OAuth login will no longer work from
        // a WebView, so Cordova apps must use Google Sign-In instead.
        // https://github.com/meteor/meteor/issues/8253
        Google.signIn(options, callback);
        return;
    } // Use Google's domain-specific login page if we want to restrict creation to

    // a particular email domain. (Don't use it if restrictCreationByEmailDomain
    // is a function.) Note that all this does is change Google's UI ---
    // accounts-base/accounts_server.js still checks server-side that the server
    // has the proper email address after the OAuth conversation.
    if (typeof Accounts._options.restrictCreationByEmailDomain === 'string') {
        options = Object.assign({}, options || {});
        options.loginUrlParameters = Object.assign({}, options.loginUrlParameters || {});
        options.loginUrlParameters.hd = Accounts._options.restrictCreationByEmailDomain;
    }

    innerLoginWithGoogleAndTOTP(options, code, callback);
};

Meteor.loginWithGoogle = (options, callback) => {
    overrideLoginMethod(loginWithGoogle, [options], callback, loginWithGoogleAndTOTP);
};

Google.requestCredential = wrapRequestCredentialFn(
    'google',
    ({ config, loginStyle, options: requestOptions, credentialRequestCompleteCallback }) => {
        const credentialToken = Random.secret();
        const options = requestOptions as Meteor.LoginWithExternalServiceOptions & {
            loginUrlParameters?: {
                include_granted_scopes?: boolean;
                hd?: string;
            };
            prompt?: string;
        };

        const scope = ['email', ...(options.requestPermissions || ['profile'])].join(' ');

        const loginUrlParameters: Record<string, any> = {
            ...options.loginUrlParameters,
            ...(options.requestOfflineToken !== undefined && {
                access_type: options.requestOfflineToken ? 'offline' : 'online',
            }),
            ...((options.prompt || options.forceApprovalPrompt) && { prompt: options.prompt || 'consent' }),
            ...(options.loginHint && { login_hint: options.loginHint }),
            response_type: 'code',
            client_id: config.clientId,
            scope,
            redirect_uri: OAuth._redirectUri('google', config),
            state: OAuth._stateParam(loginStyle, credentialToken, options.redirectUrl),
        };

        Object.assign(loginUrlParameters, {
            response_type: 'code',
            client_id: config.clientId,
            scope,
            redirect_uri: OAuth._redirectUri('google', config),
            state: OAuth._stateParam(loginStyle, credentialToken, options.redirectUrl),
        });
        const loginUrl = `https://accounts.google.com/o/oauth2/auth?${Object.keys(loginUrlParameters)
            .map((param) => `${encodeURIComponent(param)}=${encodeURIComponent(loginUrlParameters[param])}`)
            .join('&')}`;

        OAuth.launchLogin({
            loginService: 'google',
            loginStyle,
            loginUrl,
            credentialRequestCompleteCallback,
            credentialToken,
            popupOptions: { height: 600 },
        });
    },
);