Chrome extension for RingCentral Team Messaging and Salesforce

Tyler Liu
RingCentral Developers
7 min readNov 11, 2021

Today I am going to introduce a product built by me. Its name is Chrome extension for RingCentral Team Messaging and Salesforce. The product is already in production, ready to be installed from Chrome Web Store:

In this article, I am going to cover the following information:

  1. Background information and user requirements
  2. The frontend app
  3. The chrome extension

Let’s start!

Background information and user requirements

There is a true story behind this product. The credit for this product idea goes to the RingCentral sales team. Around a year ago, I got a request from one of the leaders of the sales team. He told me that they urgently needed a convenient way to connect RingCentral Team Messaging and Salesforce. They manage hundreds of Salesforce tickets, each of which has a RingCentral Team Messaging team to host the corresponding discussion.

The problem is: it is very hard to find the right RingCentral Team Messaging team for a given Salesforce ticket, and vice versa. Let’s say you are navigating a Salesforce ticket and you suddenly get an exciting idea which you’d like to share with your colleagues on RingCentral Team Messaging. But how do you find the right RingCentral Team Messaging team for this ticket? What if the team hasn’t been created yet? Likewise, when you are going through the chat history of a RingCentral Team Messaging team, you suddenly want to check the details of the corresponding Salesforce ticket. How do you find that Salesforce ticket? I know there are ways to find both. But it is important to do them efficiently because you likely have to do use them frequently.

Here comes the user requirements: whenever a user should visit a Salesforce ticket in their Chrome browser, the product (as a chrome extension) should insert a link into the page. The user then can jump to the corresponding RingCentral Team Messaging team by clicking that link. If the RingCentral Team Messaging team hasn’t been created yet, a button should be displayed instead. By clicking the button, a brand new RingCentral Team Messaging team will be created to host the discussion for the current Salesforce ticket.

OK, now on to the code!

The frontend app

Wait a minute! Why do we need a frontend app at all? Aren’t we creating a Chrome extension instead? Yes, you are right, we are. A chrome extension is also a JavaScript app and it runs inside a browser. But it is not a typical frontend app, it’s not straightforward to use our familiar technical stack, such as React and webpack. So I decided to create a traditional frontend app instead and inject it into the target page as an iframe.

The frontend app is a React app, bundled by webpack. There are several technical details that I’d like to share with you:

  1. webpack 5
  2. Authorization Code + PKCE
  3. SubX, a state container solution

webpack 5

This is the first time that I’ve used webpack 5. There is a major breaking change (compared with webpack 4.x):

So webpack 5 stops applying polyfills for node.js core modules. You need to explicitly configure the polyfills. I configured the following polyfills for my project:

And I added crypto-browserify stream-browserify and buffer as devDependencies .

There is one more issue that will prevent my app from running:

Again process is node.js only. It seems that webpack 5 stops taking care of it for us. The solution is:

And I added process to devDependencies .

OK, that’s all I’d like to cover about webpack 5.

Authorization Code + PKCE

My colleague Embbnux wrote a great article on this topic. I am not going to give you a comprehensive introduction to it. But I will tell you why I chose it. For frontend apps, there is no server side code. But we don’t want to store any confidential information on client side. For confidential information here I mean the RingCentral app clientSecret . Authorization Code + PKCE doesn’t need clientSecret at all!

For the implementation part, I used the RingCentral Extensible SDK and its Authorize URI extension.

import RingCentral from '@rc-ex/core';const rc = new RingCentral({
clientId: process.env.RINGCENTRAL_CLIENT_ID,
server: 'https://platform.ringcentral.com',
});

From the code above, you can see that I only specified clientId, I didn’t specify clientSecret . This is one of the core benefits of using PKCE. Nobody wants to deploy secrets to the client side.

import AuthorizeUriExtension from '@rc-ex/authorize-uri';
import localforage from 'localforage';
const redirectUri = window.location.origin + window.location.pathname;const authorizeUriExtension = new AuthorizeUriExtension();
rc.installExtension(authorizeUriExtension);
const authorizeUri = authorizeUriExtension.buildUri({
redirect_uri: redirectUri,
code_challenge_method: 'S256',
});
const codeVerifier = authorizeUriExtension.codeVerifier;
localforage.setItem('code_verifier', codeVerifier);

We use the @rc-ex/authorize-uri extension to help us to do OAuth. The extension is just one of the extensions from the RingCentral Extensible project. I recommend you check out the RingCentral Extensible project to see what other extensions are available.

When we build the authorizeUri, we pass the parameter code_challenge_method: ‘S256’ to indicate that we want to use PKCE instead of the regular Authorization code flow. We need to save the codeVerifier somewhere because we need it in the next step. In the code sample above, we saved it using localstorage, which is a famous browser side storage solution.

When the user authorizes our app and redirected back, the following code will be invoked:

const urlSearchParams = new URLSearchParams(new URL(window.location.href).search);
const code = urlSearchParams.get('code');
const token = await rc.authorize({
code,
redirect_uri: redirectUri,
code_verifier: (await localforage.getItem('code_verifier')) as string,
});

We get the code parameter from url query string. When we do authorize by code, we need to pass the code_verifier parameter which we saved in thelast step.

That’s it for the whole PKCE OAuth flow! The only tricky part is the code_verifier.

SubX, a state container solution

Disclaimer, the SubX project is my personal project. I created it to replace Redux. Its main advantage over Redux is it is very straightforward to use. For some reason coding a Redux app can take days, so try SubX which should only take you a few hours.

To use SubX together with React, check the react-subx project. Here is some sample code for your reference:

import SubX from 'subx'
import { Component } from 'react-subx'
import React from 'react'
import ReactDOM from 'react-dom'

const store = SubX.create({
number: 0,
decrease () {
this.number -= 1
},
increase () {
this.number += 1
}
})

class App extends Component {
render () {
const store = this.props.store
return <div>
<button onClick={e => store.decrease()}>-</button>
<span>{store.number}</span>
<button onClick={e => store.increase()}>+</button>
</div>
}
}

ReactDOM.render(<App store={store} />, document.getElementById('container'));

You can see that the code is way more readable than its Redux counterpart. It is more noticeable for bigger projects.

The Chrome extension

Finally it’s time to talk about the Chrome extension. If you have never created a Chrome extension, here is the quick start tutorial. The Chrome extension is kind of simple because most of the code is in the frontend app. Here is the code of the manifest.json file:

{
"name": "RingCentral Team Messaging Salesforce",
"description": "Link Salesforce cases to RingCentral Team Messaging teams",
"version": "3.5",
"manifest_version": 2,
"content_scripts": [
{
"matches": [
"https://*.salesforce.com/5*",
"https://*.lightning.force.com/lightning/*"
],
"js": ["content.js"]
}
],
"icons": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
}

You can see that there is only one source code file which is the content.js file. What should we do in this source code file? Easy, we just embed our frontend app as an iframe:

const containerNode = document.createElement('div')
flexPage.prepend(containerNode)
containerNode.innerHTML = `<iframe frameBorder="0" width="100%" height="24" src="https://ringcentral.github.io/chrome-extension-glip-salesforce/?${urlSearchParams.toString()}"></iframe>`

But how do we pass data from Salesforce to RingCentral Team Messaging? The answer is to use URLSearchParams :

const urlSearchParams = new URLSearchParams()
urlSearchParams.append('keyword', caseId)
urlSearchParams.append('teamName', `${accountName}: Case ${caseId} ${subject}`)
urlSearchParams.append('sfTicketUri', window.location.origin + window.location.pathname)

How do we get the data fields? Such as the caseId and accountName? The answer is regular expressions. How to use regular expression to extract data from an HTML page is out of the scope of this article.

The source code

Last but not least, here is the full source code for your reference. We developers can learn more from the source code than from this article. So please help yourself. Please let us know what you think by leaving your questions and comments below. To learn even more about other features we have make sure to visit our developer site and if you’re ever stuck make sure to go to our developer forum.

Want to stay up to date and in the know about new APIs and features? Join our Game Changer Program and earn great rewards for building your skills and learning more about RingCentral!

--

--