import { Users } from '@rocket.chat/models'; import { serverFetch as fetch } from '@rocket.chat/server-fetch'; import { Meteor } from 'meteor/meteor'; import { getRedirectUri } from './getRedirectUri'; import { userScopes } from './oauthScopes'; import { assertNotOfflineLicense } from './offlineLicense'; import { settings } from '../../settings'; import { SystemLogger } from '../logger/system'; export async function finishOAuthAuthorization(code: string, state: string) { assertNotOfflineLicense(); if (settings.get('Cloud_Workspace_Registration_State') !== state) { throw new Meteor.Error('error-invalid-state', 'Invalid state provided', { method: 'cloud:finishOAuthAuthorization', }); } const clientId = settings.get('Cloud_Workspace_Client_Id'); const clientSecret = settings.get('Cloud_Workspace_Client_Secret'); const scope = userScopes.join(' '); let payload; try { const cloudUrl = settings.get('Cloud_Url'); const response = await fetch(`${cloudUrl}/api/oauth/token`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, params: new URLSearchParams({ client_id: clientId, client_secret: clientSecret, grant_type: 'authorization_code', scope, code, redirect_uri: getRedirectUri(), }), // SECURITY: the URL is a default hardcoded value or an envvar/setting set by an admin. It's safe to disable this check. ignoreSsrfValidation: true, }); if (!response.ok) { throw new Error((await response.json()).error); } payload = await response.json(); } catch (err) { SystemLogger.error({ msg: 'Failed to finish OAuth authorization with Rocket.Chat Cloud', url: '/api/oauth/token', err, }); return false; } const expiresAt = new Date(); expiresAt.setSeconds(expiresAt.getSeconds() + payload.expires_in); const uid = Meteor.userId(); if (!uid) { throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'cloud:finishOAuthAuthorization', }); } await Users.updateOne( { _id: uid }, { $set: { 'services.cloud': { accessToken: payload.access_token, expiresAt, scope: payload.scope, tokenType: payload.token_type, refreshToken: payload.refresh_token, }, }, }, ); return true; }