That said, deploying a first version with an empty handler is a good way to test messages are being received correctly. In a real world application we’d add some code here. This has a stub implementation, left empty for the purposes of this example. This ensures we have it logged even if we crash later. We store the data in the AcmeWebhookMessage model before attempting to process it. Since we’ve verified the message is from Acme, if the body is not JSON, something has gone wrong, and we’d like to know about it. This is a fine failure mode for our example. If an error does occur, the view will crash, and our error reporting software (e.g. We do this without any checking of the Content-Type header or error handling if the body isn’t valid JSON. We use json.loads() to load the request body. In this case we could move the deletion out to a periodic background task, similar to Django’s clearsessions. If our webhook ends up running frequently, executing this delete query each time may get expensive. īefore storing the new message, we clean up stored messages older than a week.If you’re adapting this code, check your caller’s documentation. Since there’s no real standard for webhooks, different callers use different authentication methods. (Thanks to Florian Apolloner for reminding me to add this protection.)Īuthentication is very important for webhook receivers since they are on the public web, and anyone could potentially discover them. This prevents timing attacks from retrieving our secret token. Unlike normal string comparison, this is guaranteed to take the same amount of time no matter the input string. We use pare_digest() to perform the comparison. If the two do not match, we can reject the incoming message. We check this header against the token they should be using, which we store in an environment variable and read in our settings. Īcme’s system provides some authentication with a token in the Acme-Webhook-Token header.Therefore we don’t want a transaction around the whole view. Here, we’re using direct transaction control-the on process_webhook_payload-to ensure that if our business logic crashes, we’ve at least saved the AcmeWebhookMessage for debugging. Using ATOMIC_REQUESTS is normally a good idea, and a straightforward way of adding transactions to your Django application. But for webhooks, we verify requests with different authentication schemes, so we can disable blocks non-POST disables the ATOMIC_REQUESTS (transaction-per-request) for this view. Normally we wouldn’t want to accept a POST request without a CSRF token, as it could indicate a user being tricked into submitting a malicious form to our site from another. now (), payload = payload, ) process_webhook_payload ( payload ) return HttpResponse ( "Message received okay.", content_type = "text/plain" ) def process_webhook_payload ( payload ): # TODO: business logic disables Django’s default cross-site request forgery (CSRF) protection. ACME_WEBHOOK_TOKEN ): return HttpResponseForbidden ( "Incorrect token in Acme-Webhook-Token header.", content_type = "text/plain", ) AcmeWebhookMessage. get ( "Acme-Webhook-Token", "" ) if not compare_digest ( given_token, settings. Import datetime as dt import json from secrets import compare_digest from nf import settings from django.db.transaction import atomic, non_atomic_requests from django.http import HttpResponse, HttpResponseForbidden from import csrf_exempt from import require_POST from django.utils import timezone from import AcmeWebhookMessage def acme_webhook ( request ): given_token = request.
0 Comments
Leave a Reply. |
Details
AuthorWrite something about yourself. No need to be fancy, just an overview. ArchivesCategories |