Usage
Introduction
With the release of ARK Core 2.0, a new feature was introduced, called Webhooks which allows you to create more flexible and automated systems while also reducing traffic/load on your server.
Authorization
Before we start working on the implementation of a webhook handler, we will take a look at handling authorization.
To guarantee that only your server is allowed to send data to your webhook handler, an authorization token is generated on creation of a webhook. The generated token will only be returned once and not be visible again.
To generate an authorization token, you need to create a webhook.
Lets take the following token as an example fe944e318edb02b979d6bf0c87978b640c8e74e1cbfe36404386d33a5bbd8b66
which is 64 characters long and breaks down into 2 parts at 32 characters length each.
The first 32 characters will be stored in the database and sent to you as a header Authorization: fe944e318edb02b979d6bf0c87978b64
via a POST request.
The last 32 characters 0c8e74e1cbfe36404386d33a5bbd8b66
need to be stored by you and will serve as a way for you to verify that the request is authorized.
Handling Webhooks
Now that we know how the token is structured and what it is used for we can continue with implementing a webhook handler.
A webhook handler is just a simple POST endpoint that you need to implement at the URL you specified when creating a webhook.
1const webhookToken = 2 "fe944e318edb02b979d6bf0c87978b640c8e74e1cbfe36404386d33a5bbd8b66"; 3 4const verification = "0c8e74e1cbfe36404386d33a5bbd8b66"; 5 6server.post("/blocks", jsonParser, (req, res) => { 7 // This will be fe944e318edb02b979d6bf0c87978b64 8 const authorization = req.headers["authorization"]; 9 10 // This will be authorization + verification11 const token = authorization + verification;12 13 // Make sure we block access if the token is invalid...14 if (token !== webhookToken) {15 return res.status(401).send("Unauthorized!");16 }17 18 // the datetime of when the webhook was sent19 console.log(req.body.created);20 21 // the data the webhook transferred, e.g. a block struct22 console.log(req.body.data);23 24 // the type of event that was sent, e.g. block.forged25 console.log(req.body.type);26 27 // do something with the above req.body data28 29 return res.status(200).send("Hello Webhook!");30});
1package main 2 3import ( 4 "fmt" 5 "log" 6 "net/http" 7) 8 9const (10 webhookToken = "fe944e318edb02b979d6bf0c87978b640c8e74e1cbfe36404386d33a5bbd8b66"11 verification = "0c8e74e1cbfe36404386d33a5bbd8b66"12)13 14func validateOrigin(next http.Handler) http.Handler {15 return func(w http.ResponseWriter, r *http.Request) {16 if r.Header.Get("authorization") + verification != webhookToken {17 w.WriteHeader(http.StatusUnauthorized)18 w.Write([]byte("Unauthorized!"))19 return20 }21 return next(w, r)22 }23}24 25func handler(w http.ResponseWriter, r *http.Request) {26 decoder := json.NewDecoder(r.Body)27 28 var resp Response // some defined DTO29 err := decoder.Decode(&resp)30 if err != nil {31 handle(w, err)32 }33 34 // do something with the received block/transaction/wallet35 36}37 38func main() {39 http.HandleFunc("/blocks", validateOrigin(handler))40 log.Fatal(http.ListenAndServe(":8080", nil))41}
1import pickle 2import hashlib 3 4from flask import Flask, request 5from werkzeug.exceptions import Unauthorized 6from functools import wraps 7 8app = Flask(__name__) 9 10def dump_webhook_token(token):11 authorization = token[:32] # "fe944e318edb02b979d6bf0c87978b64"12 verification = token[32:] # "0c8e74e1cbfe36404386d33a5bbd8b66"13 filename = hashlib.md5(authorization.encode("utf-8")).hexdigest()14 with open(filename, "wb") as out:15 pickle.dump(16 {17 "verification": verification,18 "hash": hashlib.sha256(token.encode("utf-8")).hexdigest()19 },20 out21 )22 23 24def check_webhook_token(authorization):25 filename = hashlib.md5(authorization.encode("utf-8")).hexdigest()26 try:27 with open(filename, "rb") as in_:28 data = pickle.load(in_)29 except Exception:30 return False31 else:32 token = authorization + data["verification"]33 return hashlib.sha256(34 token.encode("utf-8")35 ).hexdigest() == data["hash"]36 37 38# ...39# Somewhere On Webhook Subscription40dump_webhook_token(41 "fe944e318edb02b979d6bf0c87978b640c8e74e1cbfe36404386d33a5bbd8b66"42)43# verification = "0c8e74e1cbfe36404386d33a5bbd8b66"44# token = "fe944e318edb02b979d6bf0c87978b640c8e74e1cbfe36404386d33a5bbd8b66"45 46 47#...48# This Should Be Middleware if This App Is Dedicated to Webhooks49def token_required(f):50 @wraps(f)51 def decorated_function(*args, **kwargs):52 # if request.headers.get("authorization") + verification != token:53 if not check_webhook_token(request.headers.get("authorization")):54 raise Unauthorized("Unauthorized!")55 return f(*args, **kwargs)56 return decorated_function57 58 59@app.route("/blocks")60@token_required61def handle_block():62 block = request.get_json()63 # do something with the block64 65 66if __name__ == "__main__":67 app.run(debug=True, port=5000)
Let’s break down the steps we took here:
- Grab the
Authorization
header. - Create the full token based on the
Authorization
header andVerification
string. - Deny access if the
full token
does not equal thewebhook token
. - Log and process the request body if the
full token
is valid.
Authentication
To communicate with the Webhooks API, you will need to provide the token you configured on your node through the Authorization
header. Authenticating with an invalid token will return 401 Unauthorized
.
Headers
Name | Type | Description | Required |
---|---|---|---|
Authorization | string | The webhook token defined in the node configuration. | Yes |