There is a bug in Ktor 1.x.x currently (fixed in 2.0.0 I believe) where the behaviour of call.receiveText() can potentially lead to invalid request signature matching on event requests sent by an Enterprise Grid workspace.
For example, a lot of Slack users might use the Google/Outlook calendar apps to sync Slack with their meetings. These plugins both set the user status text to be In a meeting • Google/Outlook Calendar. When this is sent from a normal workspace, the event contains a unicode code point (\u2022) for the dot. When sent from a grid, the dot is sent exactly as is.
The bolt-ktor function toBoltRequest uses call.receiveText() to get the request body for the event. But because the request does not come with an explicit charset applied to the Content-Type header, Ktor's receiveText function ignores the fact that the data is json and uses default plaintext encoding using ISO_8859-1. This turns the dot into � which changes the bytes. This then fails the request verifier stage.
The workaround is to enforce UTF-8 encoding on requests with a Content-Type of application/json unless a specific charset is applied. Here is the Ktor youtrack issue. We used the following workaround from there:
/**
* Receive the request as String.
* If there is no Content-Type in the HTTP header specified use ISO_8859_1 as default charset, see https://www.w3.org/International/articles/http-charset/index#charset.
* But use UTF-8 as default charset for application/json, see https://tools.ietf.org/html/rfc4627#section-3
*/
private suspend fun ApplicationCall.receiveTextWithCorrectEncoding(): String {
fun ContentType.defaultCharset(): Charset = when (this) {
ContentType.Application.Json -> Charsets.UTF_8
else -> Charsets.ISO_8859_1
}
val contentType = request.contentType()
val suitableCharset = contentType.charset() ?: contentType.defaultCharset()
return receiveStream().bufferedReader(charset = suitableCharset).readText()
}
Reproducible in:
The Slack SDK version
Latest (v1.21.1 at time of writing)
Java Runtime version
Java 11 (Azul and Coretto) is what we're using
Steps to reproduce:
- Create a simple Slack app using Ktor < 2.0.0
- Setup app to use user_change event
- Install the app in an enterprise grid workspace
- Update your profile status in the enterprise grid to say In a meeting • Google Calendar
Expected result:
Event should be sent to Slack app without issue and not produce a 401 due to invalid signature
Actual result:
Request will fail with an invalid request signature and a 401
There is a bug in Ktor 1.x.x currently (fixed in 2.0.0 I believe) where the behaviour of
call.receiveText()can potentially lead to invalid request signature matching on event requests sent by an Enterprise Grid workspace.For example, a lot of Slack users might use the Google/Outlook calendar apps to sync Slack with their meetings. These plugins both set the user status text to be In a meeting • Google/Outlook Calendar. When this is sent from a normal workspace, the event contains a unicode code point (
\u2022) for the dot. When sent from a grid, the dot is sent exactly as is.The bolt-ktor function
toBoltRequestusescall.receiveText()to get the request body for the event. But because the request does not come with an explicit charset applied to theContent-Typeheader, Ktor's receiveText function ignores the fact that the data is json and uses default plaintext encoding using ISO_8859-1. This turns the dot into�which changes the bytes. This then fails the request verifier stage.The workaround is to enforce UTF-8 encoding on requests with a
Content-Typeofapplication/jsonunless a specific charset is applied. Here is the Ktor youtrack issue. We used the following workaround from there:Reproducible in:
The Slack SDK version
Latest (v1.21.1 at time of writing)
Java Runtime version
Java 11 (Azul and Coretto) is what we're using
Steps to reproduce:
Expected result:
Event should be sent to Slack app without issue and not produce a 401 due to invalid signature
Actual result:
Request will fail with an invalid request signature and a 401