Fortunately its pretty easy to implement. The documentation about validating the x-xero-signature is as good as useless but hopefully the code below will help any future implementors get it sorted! I just ported the .NET code to Go and its working correctly.
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"fmt"
"io/ioutil"
"net/http"
)
func XeroWebhookHandler(w http.ResponseWriter, r *http.Request) {
xeroSignature := r.Header.Get("x-xero-signature")
if xeroSignature == "" {
http.Error(w, "Xero webhook endpoint got hit without a 'x-xero-signature' header", 400)
return
}
defer r.Body.Close()
//Because this endpoint will be unauthenticated, be careful reading the entire body
//To minimise any DDoS risk only read up to a certain size (50kB)
bodyReaderWithSizeLimit := http.MaxBytesReader(w, r.Body, 50*1024)
bodyBytes, err := ioutil.ReadAll(bodyReaderWithSizeLimit)
if err != nil {
http.Error(w, "webhook had unreadable body.", 500)
return
}
//Store the xeroWebhookKey somewhere safe
xeroWebhookKey := "Get this value from the webhook config on developer.xero.com"
expectedSignature := GetExpectedXeroSignature(bodyBytes, xeroWebhookKey)
signatureGood := xeroSignature == expectedSignature
if !signatureGood {
fmt.Println("Xero Failed to verify xero signature")
w.WriteHeader(401)
return
}
//Process webhook
fmt.Println("Xero webhook recieved")
fmt.Println(string(bodyBytes))
w.WriteHeader(200)
}
func GetExpectedXeroSignature(requestBodyBytes []byte, clientSecret string) string {
key := []byte(clientSecret)
hash := hmac.New(sha256.New, key)
hash.Write(requestBodyBytes)
return base64.StdEncoding.EncodeToString(hash.Sum(nil))
}
This SDK doesn't have any support for web-hooks.
Fortunately its pretty easy to implement. The documentation about validating the
x-xero-signature
is as good as useless but hopefully the code below will help any future implementors get it sorted! I just ported the .NET code to Go and its working correctly.