In this case, I start with the Play Framework (2.5 and Scala), and I'm trying to learn by creating a bot for the Facebook messenger. However, I was stuck trying to verify the message signature.
I followed the documentation on Facebook and created a webhook. Which handles POST requests with getRawMessages(see code below). He then tries to verify that the request is signed by Facebook using the function verifyPayload. However, I cannot get the computed and actual hashes to match.
I took the initiative to look at this question: How to authenticate Instagram X-hub signature in Java? which seems to be doing pretty much what I want, but for the equivalent of Instagram. But I still can’t understand that this is correct.
val secret = "<facebooks secret token>"
def getRawMessages = Action (parse.raw) {
request =>
val xHubSignatureOption = request.headers.get("X-Hub-Signature")
try {
for {
signature <- xHubSignatureOption
rawBodyAsBytes <- request.body.asBytes()
} yield {
val rawBody = rawBodyAsBytes.toArray[Byte]
val incomingHash = signature.split("=").last
val verified = verifyPayload(rawBody, secret, incomingHash)
Logger.info(s"Was verified? $verified")
}
Ok("Test")
}
catch {
case _ => Ok("Test")
}
}
val HMAC_SHA1_ALGORITHM = "HmacSHA1"
def verifyPayload(payloadBytes: Array[Byte], secret: String, expected: String): Boolean = {
val secretKeySpec = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), HMAC_SHA1_ALGORITHM)
val mac = Mac.getInstance(HMAC_SHA1_ALGORITHM)
mac.init(secretKeySpec)
val result = mac.doFinal(payloadBytes)
val computedHash = Hex.encodeHex(result).mkString
Logger.info(s"Computed hash: $computedHash")
computedHash == expected
}
Facebook webhook docs :
HTTP- X-Hub-Signature, SHA1 , sha1 =.
, unicode , . , . , äöå \ U00e4\u00f6\u00e5.
, , , - unicode, . , , (jsonRawBytes = jsonRaw.asBytes();).
, , .