Introduction

This is the second part of this series posts about creating Slack Command APIs with Ruby. In the first post, we’ve setup a new application that returns OK to the command caller.

In this post, we’re going to talk about how to authorize Slack Commands requests in order to avoid invalid requests in our API.

Validating the request via token

According to Slack Commands API documentation:

When someone types a slash command, the message (and its data) will be sent to the configured external URL via HTTP POST. It's up to you, the developer, to do something with the message data and respond back, if desired.

The POST body message has the following params structure:

{
"token"=>"XXXXX",
"team_id"=>"YYYY",
"team_domain"=>"ZZZZ",
"channel_id"=>"UUUU",
"channel_name"=>"directmessage",
"user_id"=>"U1234567",
"user_name"=>"anderson",
"command"=>"/congratulate",
"text"=>"@john for his new product release! It's brilliant!",
"response_url"=>"https://hooks.slack.com/commands/YYYY/DDDDD/HASH"
}

And, It’s our responsability to check if the message is valid or not “by confirming that the token value matches the validation token you received from Slack when creating the command.”

Server-side validation

We’re going to create a Rack Middleware that intercepts our request and validates the token param.

If the token matches, we should continue to return “OK” to the user. If the token is invalid we must return a specific message telling the Slack user to setup the correct token in the application.

The Rack Middleware

Rack Middlewares are useful to intercept requests without changing the actual handler method. In this case, we’re going to create a Middleware called SlackAuthorizer.

It needs to implement two methods:

  • initialize(app): responsible for receiving the application context
  • call(env): responsible for handling the actual request, it must return a valid rack message, composed by an array with the status code, header hash and an array with the body message.

Here is the actual implementation of the SlackAuthorizer class:

# app/slack_authorizer.rb
class SlackAuthorizer
UNAUTHORIZED_MESSAGE = 'Ops! Looks like the application is not authorized! Please review the token configuration.'.freeze
UNAUTHORIZED_RESPONSE = ['200', {'Content-Type' => 'text'}, [UNAUTHORIZED_MESSAGE]]
  def initialize(app)
@app = app
end
  def call(env)
req = Rack::Request.new(env)
if req.params['token'] == ENV['SLACK_TOKEN']
@app.call(env)
else
UNAUTHORIZED_RESPONSE
end
end
end

As you may see if the token param matches the SLACK_TOKEN env variable we send the request back to the application, else we should respond with the unauthorized rack response.

Notice that we respond with a 200 status. That’s because Slack expects 200 responses, even when it has any unexpected behavior we should respond with 200 status for consistency.

Plug in the Middleware

The next step is to use the Middleware in the application. Sinatra makes it easy. We need to literally use it inside the body of our Sinatra application:

require 'sinatra'
require_relative 'app/slack_authorizer'
use SlackAuthorizer
post '/slack/command' do
'OK'
end

Setup the token in production

The final step is to deploy the application, setup the token env var and test it.

In order to deploy a new version of the application we must send it to Heroku via git:

$ git add .
$ git commit -m 'Adding token authentication'
$ git push heroku master

OK. The new application version is now deployed, but we’ve not configured the token already. Let’s test if the middleware is up.

Run the following command in Slack:

/congratulate @john for doing a great job!

You must see something like that:

Middleware generated command response.

Great! Let’s setup the token env var in the Heroku application:

Copy the “Verification Token” from “Slack Command App Credentials” and save it as a new “Config Variable” in Heroku’s application

Test it

Now everything is setup, let’s send the /congratulate command again and see what happens:

/congratulate @john for doing a great job!
Command returns: “OK”.

It means we’ve configured the correct verification token in Heroku.

Conclusion

Leaving the API open to any request is insecure and it’s up to us to protect the application against these invalid POST messages.

In a real world application, you’ll not use the Middleware the way we did it because it’s going to require the token in any route call. We can create multiple Sinatra::Base applications in the same Sinatra app and use the Middleware in the one responsible for slack commands, for instance.

In the next post will see how to validate the income params and how to return the correct feedback to users.

Follow the step by step coding at:


One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.