java-slack-sdk: Failing to parse request body when using along with Spring Boot 2.1

Reproducible in:

The Slack SDK version

Note: I’ve tried several versions of bolt/bolt-servlet between 1.0.0-1.2.0 inclusive and gotten the same behavior.

[INFO] +- com.slack.api:bolt:jar:1.1.5:compile
[INFO] |  +- com.slack.api:slack-api-model:jar:1.1.5:compile
[INFO] |  +- com.slack.api:slack-api-client:jar:1.1.5:compile
[INFO] |  \- com.slack.api:slack-app-backend:jar:1.1.5:compile
[INFO] +- com.slack.api:bolt-servlet:jar:1.1.5:compile

Java Runtime version

Java 8 SE 1.8.0_221

OS info

So far I have only tested this in local dev environments using ngrok to communicate with Slack. I don’t expect this is the cause of the problem, but just in case:

ProductName:	Mac OS X
ProductVersion:	10.14.6
BuildVersion:	18G95
Darwin Kernel Version 18.7.0: Tue Aug 20 16:57:14 PDT 2019; root:xnu-4903.271.2~2/RELEASE_X86_64

Steps to reproduce:

I am trying to do a slack command integration. The server receives the HTTP POST request from Slack that appears to contain all the correct information and in the correct format according to the API documentation, but my handler never actually gets executed.

Expected result:

My slash command handler defined below to get executed:

        app.command("/do-the-thing", (req, context) -> {
            
            SlashCommandPayload command = req.getPayload();
            
            slackMessageHandler.handleSlashCommand(command, context);
            
            return context.ack();
        });

Actual result:

The Slack Request parser receives an empty string as a body, and doesn’t know what to do with it. Bolt acknowledges the request with a 200 but doesn’t do anything because it doesn’t recognize the request as a slash command.

 WARN  c.s.api.bolt.util.SlackRequestParser - No request pattern detected for 

(blank, because the body given to the method is blank.)

I did some debugging and found that everything appears to be going fine until this line. The HttpServletRequest has the information in its reader, but this method fails to extract it. Ultimately, a blank String is sent as the body to SlackRequestParser.parse

Other notes

As an experiment, when I mock a mostly identical request to our URL where the only change is changing content-type Header to text/plain as opposed to x-www-form-urlencoded it works, or at least, the parser a) receives a body with actual content and b) recognizes the content as a slash command.

This issue is also unique to slash commands. I am able to successfully receive and process events pushed to our URL from the events API.

Our basic code structure:

//SlackBotApplication.java
@SpringBootApplication
@EnableConfigurationProperties
@ServletComponentScan
public class SlackBotApplication {
 
    public static void main(String[] args) {
        try {
            ConfigurableApplicationContext applicationContext = new SpringApplicationBuilder()
                    .sources(SlackBotApplication.class)
                    .run(args);
            Thread.currentThread().join();
        } catch (Exception e) {
            System.out.println("Something went wrong " + e.getMessage());
}
}        }
    }
 
//SlackBotController.java
@WebServlet("/slack/events")
public class SlackAppController extends SlackAppServlet {
    public SlackAppController(App app) {
        super(app);
    }
}
 
//SlackBotConfigs.java
@Configuration
public class SlackBotConfigs {
    
    ...
    
    @Bean
    public App initSlackApp(SlackMessageHandler slackMessageHandler) {
        
        AppConfig appConfig = new AppConfig();
        
        appConfig.setSigningSecret(retrieveConfigurations().getSigningSecret());
        appConfig.setSingleTeamBotToken(retrieveConfigurations().getSlackbotToken());
        
        App app = new App(appConfig);
        
        app.event(AppMentionEvent.class, (payload, ctx) ->  {
            // do some stuff
              return ctx.ack()
        });
 
      app.event(MessageBotEvent.class, (payload, ctx) ->  {
          // do some stuff
          return ctx.ack()
      });
  
      app.command("/do-the-thing", (req, context) -> {
            
          //Do some stuff. Note that this handler never gets executed as a result of the issues we are facing           
          return context.ack();
       });
   }
    ...
}

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 19 (6 by maintainers)

Most upvoted comments

The first one is more common. If some of your Spring Boot add-ons/extensions parse (=in other words, they consume the InputStream of request body) under the hood, your Slack app endpoint is unable to know the valid request body data (as you may know, this is Servlets’ limitation). Also, this is why we encourage using @WebServet type controllers for Bolt apps. Your web controller needs the access to the unmodified request body (InputStream) in HttpServletRequest.

Thanks for the feedback, @seratch. I’m using a @WebServlet controller. My suspicion is that one of the request filters in the chain is consuming the input stream before it gets to the controller, but we have dozens of filters, so it’s taking a while to figure out which one is to blame. 😓

In my case it was OAuth filter, for anyone struggling with the issue

I found a clean and, I believe, the correct solution for the problem. As some have already discovered Spring Security is to blame:

OAuth2AuthenticationProcessingFilter.doFilter() -> tokenExtractor.extractToken() -> request.getParameter()

Inside request.getParameter(), you guessed it, the stream is read.

Since the Slack Api deals with its own authentication, we shouldn’t even be processing any of the Spring Security Filter in the first place. Luckily Spring provides a proper way to to do this (Source: https://stackoverflow.com/questions/40475620/spring-security-exclude-url-on-custom-filter):

public void configure​(WebSecurity web)
    web.ignoring().antMatchers("/slack/**");
}

The first one is more common. If some of your Spring Boot add-ons/extensions parse (=in other words, they consume the InputStream of request body) under the hood, your Slack app endpoint is unable to know the valid request body data (as you may know, this is Servlets’ limitation). Also, this is why we encourage using @WebServet type controllers for Bolt apps. Your web controller needs the access to the unmodified request body (InputStream) in HttpServletRequest.

Thanks for the feedback, @seratch. I’m using a @WebServlet controller. My suspicion is that one of the request filters in the chain is consuming the input stream before it gets to the controller, but we have dozens of filters, so it’s taking a while to figure out which one is to blame. 😓

@luispollo Just in case, I’ve verified that this issue does not occur with Spring Boot 2.3.11 itself. Here is a tiny app I used for checking it: https://gist.github.com/seratch/00eb210bbab6336a0c6b2304999bbb94 Thus, there should be a particular reason in your application.

Do you have any examples of features that might affect form requests?

The common causes of the situation described here are:

  • Lack of valid string request body (= unmodified as-is string data of request body)
  • Invalid system clock settings in the host your app is running

The first one is more common. If some of your Spring Boot add-ons/extensions parse (=in other words, they consume the InputStream of request body) under the hood, your Slack app endpoint is unable to know the valid request body data (as you may know, this is Servlets’ limitation). Also, this is why we encourage using @WebServet type controllers for Bolt apps. Your web controller needs the access to the unmodified request body (InputStream) in HttpServletRequest.

I hope this was helpful to you and you’ll figure out the cause of the situation in your app!

@seratch We were able to get our application working with Spring 2.2.8. Thanks so much for your help! I’ll close this question now.