I'm something of a beginner with the Play Framework (2.5 and Scala in this case) - and I'm trying to learn by building a bot for Facebook messenger. However I've gotten stuck trying to verify the signature of the messages.
I've followed the Facebook documentation and created a webhook. Which handles POST requests usinggetRawMessages
(see code below). This then tries to verify that the request is signed by Facebook using the verifyPayload
function. However I can't seem to get the computed and the actual hashes to match up.
I've taken a lead from looking at this question: How to verify Instagram real-time API x-hub-signature in Java? which seems to be doing pretty much what I want, but for the Instagram equivalent. But I still can't seem to get it right.
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
}
The Facebook webhook docs state:
The HTTP request will contain an X-Hub-Signature header which contains the SHA1 signature of the request payload, using the app secret as the key, and prefixed with sha1=. Your callback endpoint can verify this signature to validate the integrity and origin of the payload
Please note that the calculation is made on the escaped unicode version of the payload, with lower case hex digits. If you just calculate against the decoded bytes, you will end up with a different signature. For example, the string äöå should be escaped to \u00e4\u00f6\u00e5.
I'm guessing that what I'm missing is getting the payload properly escaped to unicode, but I can't seem to find a way to do it. And the answer in the referenced question also appeared to just get the byte array without doing anything more with it (jsonRawBytes = jsonRaw.asBytes();
).
Any help with how to proceed would be much appreciated.
X-Hub-Signature is a compact way to validate real-time updates, such as webhooks from Facebook and GitHub.
Webhooks allows you to receive real-time HTTP notifications of changes to specific objects in the Facebook Social Graph. For example, we could send you a notification when any of your app Users change their email address or whenever they comment on your Facebook Page.
A webhook is a lightweight API that powers one-way data sharing triggered by events. Together, they enable applications to share data and functionality, and turn the web into something greater than the sum of its parts.
Turns out I was using the wrong secret all along. Should anyone else make the same mistake, please note that it "App Secret" that is available on the application dashboard that you want. See the screenshot below.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With