Calling the Slack API from a Cloudflare App

Today, we’re going to show you how to use Slack’s API with Cloudflare Apps. Cloudflare Apps is a platform that allows developers to create website plugins that they can sell to owners of over 7 million domains. Installers can customize the plugin to fit their website by setting options that the developers expose.

We’ll walk through creating an app that will allow visitors of the installer’s website to send a thank-you message to an app installer’s Slack Channel! We’ll do this by setting up OAuth for the app and using Slack’s Incoming Webhooks feature.

Prereqs
Slack workspace
Cloudflare account
A domain on Cloudflare

Table of Contents
Sign up for Slack’s API
Configuring a Cloudflare App Service
Building the Cloudflare App

  1. Sign up for Slack’s API

Before we can use Slack’s API, we’ll need to register our app and provide a few details. This will allow us to use OAuth for the application in order to get the app installer’s Slack account information, allowing visitors to post a thank you to the app installer’s Slack channel.

If you don’t have a Slack account already, start by registering an account and creating a new Slack Workspace. Then, navigate to the “register an app” page.

When using OAuth, you can let a potential user know how much access your app needs by setting scopes. We need access to the incoming webhook our app will generate, so let’s set the scope for the app to incoming-webhook. When we generate information through OAuth, Slack will need to know where to send it, so set the redirect url to https://www.cloudflare.com/apps/oauth/. This tells Slack to give the OAuth token and webhook to Cloudflare.

You’ll be given a Client ID and Client Secret by Slack. Cloudflare will send these to Slack when it starts the OAuth flow so that Slack knows what app OAuth is being initiated for. Remember those for when we set up our Cloudflare Apps service.

  1. Create Service in Cloudflare Apps

We’ll need to provide some info from the Slack API to connect our app to their OAuth login. We’ll put in the Client ID and Client Secret, along with the incoming-webhook scope we set for the app earlier in Slack. Fill out all the other information to let people know what your service does.

Field Value
OAuth Link https://www.slack.com/oauth/authorize
OAuth Client ID via Slack App Information
OAuth Scope incoming-webhook
OAuth Token URL https://slack.com/api/oauth.access
OAuth Client Secret via Slack App Information

note
OAuth Scope lets Slack’s API know which permissions our app is requesting. Getting other information may require different scopes, Check out their docs on scopes to learn more.

  1. Create the Cloudflare App

Before doing anything, let’s look at the App Creator and get an idea of what we’re working with. The App Creator is how we can test the code for our app live. Let’s start by downloading the example app zip file, extracting if, and uploading the folder in the App Creator.

On the left, you’ll see a panel with a box in it. The panel hosts customizable options that a user can set when they install an app. We declare these panels in the properties object of the options object inside the install.json file in the project.

Let’s expose the option for people to log into their Slack account via OAuth. We’ll do this by creating an account option that links to the Cloudflare Service we created earlier. Then, we’ll create an object inside of our hooks array that will send the OAuth initiation request to a specified endpoint so we can extract the Slack Webhook. We’ll set up that endpoint in the next step. But before we do, change the order of location to 1 so it appears after the Account option and delete the message option.

{
  "hooks": [
    {
      "endpoint": "https://yourcloudflaredomain.com/oauth",
      "events": ["option-change:account"],
      "block": true,
      "authenticate": ["account"]
    }
  ],
  "options": {
    "properties": {
      "account": {
        "title": "Slack Account",
        "type": "object",
        "format": "account",
        "services": ["{{your service ID}}"],
        "order": 0
      },
	"location": {
       "title": "Message Location",
       "description": "Where should the message appear?",
       "order": 1,
       "type": "object",
       "format": "element",
       "default": {
         "selector": "body",
         "method": "prepend"
       }
     },
    }
  }
}

Now, let’s set up our endpoint with a Cloudflare Worker.

Creating a Cloudflare Worker to handle OAuth

Cloudflare Workers is Cloudflare’s new way to handle code on the edge. Workers allow you to write javascript that can listen for requests and edit them, whether it’s a get request on your homepage or a post request on a hidden path. Let’s set up a Cloudflare Worker to listen for our Cloudflare Hook from our Service so we can add the Slack Webhook to our install options.

In order to use Workers, you’ll need a domain on Cloudflare. Follow these steps if you don’t already have a domain on Cloudflare

When we receive the token, we also receive a WebHook url from Slack’s API. We’re going to extract that information using our Cloudflare Worker and send it back to our app so visitors can post the thank you message to it. Let’s look at the structure of the response from Cloudflare Apps so we know where to look for the webhook returned by Slack.

{ 
event: "[The event name]",
user: { /* The specific Cloudflare user interacting with your app */
},
site: { /* The site the app is being installed on or is currently installed on */
},
version: { /* Information about the version being installed */
},
install: { /* The install record being created or manipulated */
schema: {
properties: { /* The form fields the customer sees while installing */ }
},
options: { /* The form field values the customer has provided */ },
productId: "gold-plan" /* The product this customer has purchased if this is a paid app */
},
authentications: { /* The access tokens for account fields which were authenticated */
account: { /* A key matching the OAuth login field declared in your install.json file */
token: { /* Token with metadata. */
token: "94094f928a82c6d5171c75f52fc1818f",
type: "Bearer",
expiration: "201X-01-15T23:04:03.776190171Z",
extra: { /* All extra fields which were returned with the access token */}
}
}
},
org: { /* The organization interacting with your app */
},
app: { /* Your app */
},
metadata: { /* Additional information associated with this event */ }
}

The webhook is returned with the OAuth request, but it isn’t the token itself, we know that it will be inside of the extra key of the token object. Since we know where it will be, let’s write a Cloudflare Worker to take Cloudflare’s App information, extract the webhook from it, put it on the options so we can access it from the front-end part of the app, and then send the options back. Go to the Service Workers page and launch the editor.

On the left is an editor for writing your Javascript code. On the right is an example of that code running. Let’s write some code that will listen for the Cloudflare hook and send back the needed endpoint.

addEventListener('fetch', event => {
  event.respondWith(oauth(event.request))
})

async function oauth(request) {
  if (request.method === "POST") {
      const text = await request.text()
      const json = JSON.parse(text)
      const {url} = json.authentications.account.token.extra.incoming_webhook

      json.install.options.endpoint = url
      return new Response(JSON.stringify(json), {status: 200})
  }
  return new Response('GET OFF MY LAWN YOU CRAZY KIDS', {status: 401})
}

Click on the “Routes” tab at the top of the left pane. Here, you can select what endpoint on your domain you will listen for Cloudflare’s hook for. I recommend yourdomain.com/oauth. After you set a URL, make sure to go back to your install.json file and update the endpoint key inside of the object in the hooks array. Now when we run our app, can access the Slack Webhook inside our app through the endpoint key on the options object.

Create the Front-end

Go into the app.js file and look for the updateElement function. That’s where we’ll be doing all the work for this app. You’ll see that there’s a main element, called element, already made for us inside of the updateElement function.

According to Slack’s API, we can send a POST request to the webhook by passing a URL encoded payload as a application/x-www-form-urlencoded content type. Let’s create a button inside the main element and add an event listener that will post to Slack when the button is clicked.

function updateElement () {
   element = INSTALL.createElement(options.location, element)

   // Set the app attribute to your app's dash-delimited alias.
   element.setAttribute('app', 'slack-thank-you')
   if (options.endpoint) {
     const button = document.createElement('button')
     const image = document.createElement('div')
     const text = document.createElement('div')
     text.innerText = "Click the icon to thank us on Slack!"
     element.appendChild(button)
     button.appendChild(image)
     element.appendChild(text)
     button.addEventListener('click', (e) => {
       const text = encodeURI(`A big thank you from ${window.location.hostname}`)
       const payload = JSON.stringify({text})
       fetch(options.endpoint, {
         method: 'POST',
         headers: {
           'content-type': 'application/x-www-form-urlencoded'
         },
         body: `payload=${payload}`
       })
       .then(() => {
         element.removeChild(button)
         element.innerHTML = '<h4>Thank you! The team has been alerted of your thanks :)</h4>'
       })
       .catch(() => {
         element.removeChild(button)
         element.innerHTML = '<h4>There was a problem thanking the team. Please try again later</h4>'
       })
     })
   }
 }

Let’s add a little CSS to make the button look nicer.

cloudflare-app[app="slack-thank-you"] button{
 background-color: inherit;
 height: 2em;
 width: 2em;
 padding: 0px;
 border: none;
}

cloudflare-app[app="slack-thank-you"] button div{
 background-image: url("https://blog.cloudflare.com/content/images/2018/02/KQZeT5R.png");
 background-size: contain;
 height: 2em;
}

Let’s update the location option as well so that its name matches its functionality and only shows when someone has signed in.

"options": {
   "properties": {
     ...
     "location": {
       "title": "Button Location",
       "description": "Where should the button appear?",
       "order": 1,
       "type": "object",
       "format": "element",
       "default": {
         "selector": "body",
         "method": "prepend"
       },
       "showIf": {
         "endpoint": {
           "op": "!=",
           "value": ""
         }
       }
     }
   }
 }

If you want to learn more about options, feel free to read through our docs. For now though, let’s give our app a spin.

We’ve now made a fully functioning Cloudflare App using the Slack API.

Slack has a vast number of possible integrations with their API, and with messaging platforms quickly becoming the most prevalent way to get in touch with people online, using API’s like Slack’s with Cloudflare Apps becomes a better idea day by day. Why not sign up for slack and give apps a try?

Slack Example App Repo

Please note that if you want to release your app, you’ll need Slack to approve the app you register with them so other people can install it.