This page describes by example how you validate the signature of incoming requests
Algorithm
To allow you to validate incoming events, we are signing every request with a signature as part of the header field X-Monta-Signature
.
The value has the following format: sha1={signature}
The signature is generated using this algorithm: HMAC-SHA1 with your webhookSecret
as key (Bytes) and the request body as input.
Example
Given you have set the following secret for your Webhook config: top-secret
And assuming we are sending this (simplified) payload (Post Body): {"foo": "bar"}
We would generate this signature: sha1=ff401a885877ab7e4665f9e045f9ee2d5876fdb9
and deliver it in the header (X-Monta-Signature
).
Note: When a JsonNode is serialized back to a String, the resulting output is in compact (minified) JSON format.
Example:
{"foo": "bar"}
becomes{"foo":"bar"}
Generating the signature (Kotlin)
val secret = "top-secret"
val payloadString = "{\"foo\": \"bar\"}"
val payload = jacksonObjectMapper().readTree(payloadString) as JsonNode // JSON
val payloadBytes = payload.toString().toByteArray() // Note: mapping from JSON to String will remove white spaces, etc (JSON minify)
val signature = SignRequest.generateSignature(secret, payloadBytes)
val expectedSignature = "sha1=ff401a885877ab7e4665f9e045f9ee2d5876fdb9"
expectThat(signature).describedAs("Signatures should match").isEqualTo(expectedSignature)
// Used Methods with outputs
SIGNATURE_ALGORITHM = "HmacSHA1"
SIGNATURE_PREFIX = "sha-1"
// key = "top-secret", payload = [123, 34, 102, 111, 111, 34, 58, 32, 34, 98, 97, 114, 34, 125]
fun generateSignature(key: String, payload: ByteArray): String {
val signingKey = SecretKeySpec(key.toByteArray(), SIGNATURE_ALGORITHM) // key.toByteArray = [116, 111, 112, 45, 115, 101, 99, 114, 101, 116]
val hmac = Mac.getInstance(SIGNATURE_ALGORITHM)
hmac.init(signingKey)
// hmac.doFinal(payload) = [-41, -9, -5, 0, -109, 71, 1, 67, -91, 123, -61, -102, 61, -97, 11, -74, 31, -90, 113, 49]
// hmac.doFinal(payload).toHexString() = "ff401a885877ab7e4665f9e045f9ee2d5876fdb9"
return SIGNATURE_PREFIX + hmac.doFinal(payload).toHexString() // "sha1=ff401a885877ab7e4665f9e045f9ee2d5876fdb9"
}
// converts ByteArray to Hex String
private fun ByteArray.toHexString(): String {
val formatter = Formatter()
for (b in this) {
formatter.format("%02x", b)
}
return formatter.toString()
}
Verify yourself
You can use this endpoint to verify / comprehend how we generate signatures: Generate Signature Details