Add a Slack Chatbox Directly into Your React App
- Share:
Introduction
Like every company, we always try to be there for our users, answer questions, get feedback, and help them get the most out of our product. One of the ways we do that is by using Slack. For every Pro/Enterprise account in our app, we create a dedicated Slack channel and invite the account users to that channel.
Of course, users can just use their Slack app to communicate with us, but we wanted to make it easier for them. For this reason, we created an embedded chat box directly in our app, through which they can communicate with their dedicated Slack channel, ask questions, get support, and give us feedback. Additionally, it allows all the users from one organization to see the messages sent to the channel and the answers you provide. This way, they can get immediate support for questions they may have during their usage of our app.
In this article, we will share a basic example of building a Slack-based chat box in your frontend app. We will show an innovative way to interact with Slack APIs from the frontend app, and how to use FoAz to secure access to the Slack APIs. At the end of the reading, you'll also have a working example of a Slack-based chat box that you can use in your app.
Slack APIs
Slack is an API-first product, with a very rich set of APIs. This means everything that can be done through the UI, can be done using APIs as well. They also allow us to build an automated workflow that drives all the cool applications you see in Slack.
We will use two APIs for our chat box: One is the conversations.history API, which allows us to read the messages from the channel, and the second is the chat.postMessage API, which allows us to send messages. Both of these APIs are HTTP-based endpoints that allow us to create a chat box in our frontend app.
Looking at the documentation of the chat.postMessage endpoints, we can see that it requires a token to be sent as part of the request. We don't want to use the user's token, as not all of the app users will have access to the channel or Slack account, so we want to use a bot token. This token will belong to a bot that will send and retrieve messages from the channel.
Using such a token has two challenges we need to deal with:
We cannot expose the token in the frontend app, as it will allow anyone to find it and send messages to the channel.
We want only admin users of our app will be able to send messages to the channel. Regular users have no SLA to send support messages to the channel.
To deal with those challenges, we will use FoAz, a feature in Permit.io that allows us to call backend APIs from the frontend app and define better permissions for "root" level tokens and secrets.
FoAz
Frontend Only Authorization (Or, FoAz) solves two major problems for us:
It allows frontend apps to call backend APIs without exposing the backend API keys in the frontend app by using the user's frontend (temporary) JWT.
It allows us to define better permissions for "root" level tokens and secrets.
While you can implement FoAz yourself by following the open standard at foaz.io, we will use Permit.io to implement FoAz in our app. For the purpose of this demo, let's start by configuring our FoAz to secure the calls to the Slack APIs in Permit.io. If you haven't signup to Permit.io yet, you can do it here.
Note: In order to follow the technical demo in this article, you must have installed Node.js/npm on your computer.
Create Slack App
To call Slack APIs, we need to create a Slack app that will allow you to call their APIs and use a bot to send messages.
To create Slack app, you can follow these steps:
In https://api.slack.com - login with your Slack credentials and click Your Apps in the top right corner
Click on Create New App in the top right corner of your applications list
Your app should have the following permissions and a bot, you can configure it manually or just paste the following app manifest (change it per your needs)
{ "display_information": { "name": "Permit.io (bot)", "description": "Permit.io Announcement Bot", "background_color": "#3a2f26" }, "features": { "bot_user": { "display_name": "GreetBot", "always_online": false } }, "oauth_config": { "scopes": { "bot": [ "channels:history", "channels:read", "chat:write", "users:read" ] } }, "settings": { "org_deploy_enabled": false, "socket_mode_enabled": false, "token_rotation_enabled": false } }
Configure Chat Permissions
Let's start our tutorial by setting up the necessary configuration required to create the chat box.
Users and Roles
To create secure access, we will first need to define users and roles. In your app, you'll probably already have a way to authenticate your users, but to save time in this example, we will use Permit.io to create mock users and roles. For a full guide on setting up roles and permissions in Permit.io, check out this tutorial.
Create the following roles in Permit.io:
admin - This role will be used to allow users to send messages to the Slack channel.
user - This role will be used to allow users to read messages from the Slack channel.
Create the following users in Permit.io:
thor@foaz-chatbox.app - This user will be assigned to the admin role.
someone@foaz-chatbox.app - This user will be assigned to the user role.
Slack API Access
Now that we have the users and roles, we can configure access to the Slack APIs. In the Permit.io app sidebar menu, you'll find a page called FoAz Proxy. This page allows you to configure the access to the Slack APIs, and to define the permissions for the users and roles we created in the previous step.
Create a new FoAz Proxy by clicking the Add Configuration button.
Assign it a name, for example, Slack Chat
In the URL field, assign the following URL to read Slack messages
slack.com/api/conversations.history
In the method, choose the
POST
methodIn the
Resource
dropdown, choose to Create New and create a new resource namedChat
with the actions ofread
andsend
Choose the action
Read
for the mapping rule we just createdClick on + Mapping Rule and paste the following URL
slack.com/api/chat.postMessage
in it, choose the method post, and assignChat
andsend
as action and resourceRepeat the previous step and paste the following URL
slack.com/api/users.list
in it, choose the methodGET
, and assignChat
andread
as action and resource
Chat Permissions
Now that we have configured the building blocks of our permission model (Roles, Actions [HTTP Methods], and Resources [URLs]), we can now create the permissions for the users and roles we created in the previous step. In the Policy Editor tab, let's mark the following permissions for our two roles:
At this point, we have all our permissions configured, and we can start using the FoAz proxy to call the Slack APIs from our frontend app. Let's clone a sample widget and try to use our new chat box.
Clone the Sample Widget
To save time, we created a sample widget that you can use to build your own Slack-based chat box. Although this widget is written in React, you can use the same approach in any frontend framework you use.
Clone the following repository
git clone git@github.com:permitio/foaz-slack-example.git
In the terminal, from the same directory you just cloned the project, Install the dependencies for our demo
npm install
Configure the right Slack channel where you want to send the messages in the .env file (to get the channel ID, right-click on the channel name, choose Copy Link, and grab the ID from the link you copied)
VITE_REACT_APP_Slack_CHANNEL_ID=<YOUR_Slack_CHANNEL_ID>
Configure the Proxy ID by copying the configuration ID from Permit’s app to the relevant environment variable in the .env file.
Start the development server
npm run dev
Open the browser at http://localhost:5173, and login with the thor@foaz-chatbox.app user we created in the previous step.
At this point, you should see a chat box in the bottom right corner of the screen. If you try to send a message, you'll see that FoAz cannot authorize you to send the message, as we didn't connect our login method to the FoAz proxy yet.
Configure the Proper JWKs
FoAz uses the user's JWT token to call the backend APIs. The way that FoAz verifies the user's identity is by validating the JWT token against the public JWKs supplied by the authentication authority. This method is supported by all the major authentication providers, and you can find the JWKs URL in the authentication provider's documentation.
In our case, tho, to avoid the mess of configuring the authentication provider, we will use mock JWKs that we will configure in Permit.io. To find your JWKs configuration, in the terminal window where you started the sample widget, you should see the following message:
--- Public JWKS ---
{"keys":[{"kty":"RSA","kid":"tvPApr70xhk-RJrKgXuTeIz9KyNjrf_6NABfvh61rCA","use":"sig","alg":"RS256","e":"AQAB","n":"qgSzmsz-Astj1FjJdqqnkaW-696aQTxrjPB7U-_uk0pto4cbTrnWihNBs791Itr0MX0XX_7BO3z-SILEcyP75vDapcn0vleJ_t-Kd1uQPiNKaUClqFqN8QmoyIE7CBCp196QfgAVVBTMgGsKXwNpGFKQg35QmKhbKlA92Ahj14S3C7B0L7OhrEUk3T9tM4QIhgbnyG_xUXT7StNE3lw-3SwS1qIU1-pQXE7OPWkcv5GEnlIxXaoxqhQZrQE77YIxgrRVrA6fbQdTLhNELO_1ZWOyFIkcRr9AJX1-Ar-92n5StHIZfIwl5-sKC931k1JgkCfuxklrbrbcgmwKR_3BlQ"}]}
As this is the public key Permit.io will use to validate the JWT token, we need to configure it in Permit.io.
In Permit.io UI, go to Settings -> JWKs and add the JWKs from the logs in your relevant environment.
Let's now go back again to the sample widget, and try to send a message again. This time, you should see that the message was sent successfully.
Verify the Proper Permissions
As we mentioned before, we want to allow only admin users to send messages to the Slack channel. To verify that, let's try to login with the someone@foaz-chatbox.app user we created in the previous step, and try to send a message.
As you can see, this time the message was not sent, as the user doesn't have the proper permissions to send messages to the Slack channel. Yet, we still can read the messages from the channel, as we configured the proper permissions for the user role.
One of the coolest things you can achieve by using FoAz is permissions that do not depend on the code of your application. In this example, we can now change the checkbox in the Policy Editor tab, and allow the user role to send messages to the Slack channel. If you try to send a message again, you'll see that this time the message was sent successfully.
Use the Chat Box in Your App
Although the code we used to demonstrate the chat box is not production ready (as it lacks error handling, edge cases, etc.) you can get a good idea of how to create one yourself in your app.
In the app.tsx file, you’ll find the required elements for the interactive chat box. We base our app on material-UI which means you can customize it according to your specific UI needs. In the same file, you’ll also find the way we utilize a useSlack hook that helps us with all the data we need to get and send to the Slack APIs. Use it as-is or create your needed hook yourself.
const { slackUser, setSlackUser, messages, send, loading, sending } = useSlack();
In the useSlack file, you’ll find first the functions we used to generate mocks of the JWT and a wrapper to the fetch function that call the Slack APIs via FoAz proxy. You’ll also find the hook itself that contains the relevant function to manage all the chat flow we manage with Slack.
const proxyFetch = async (url: string, user: string, method: string, body: any) => {
const token = await generateJWT(user);
const res = await fetch(`https://proxy.api.permit.io/proxy/${PROXY_ID}?url=${url}`, {
method,
body: body ? JSON.stringify(body) : undefined,
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
const json = await res.json();
const status = res.status;
return { status, json };
}
Conclusion
At this point, you should have enough knowledge to create your own chat box and use FoAz to secure access to the Slack APIs. As the next step, we invite you to think of more creative FoAz ways to create cool new worlds in your frontend apps.
You can read more on FoAz as a standard in foaz.io, and join our community working group in Slack to contribute to the standard and the open-source project.
Happy FoAzing!
Written by
Gabriel L. Manor
Full-Stack Software Technical Leader | Security, JavaScript, DevRel, OPA | Writer and Public Speaker