> It looks like WooCommerce makes a POST request, so the values posted
> should end up in request.post_vars.
maybe I don't understand... what I think I need to check is the raw body
of the request... isn't it? How should I check the request.post_vars?
Isn't it a dictionary or a Storage object?
> and I don't think there is much gained by putting it inside
ok... but why not?
You could parse the request body yourself, but web2py will do it automatically and put the variables in request.post_vars (if JSON is posted, its keys will become the keys of request.post_vars).
I'm not sure what you mean by "check the request.post_vars". If there are variables you are expecting in the posted body, they will be in request.post_vars. Looking at the example log here, it looks like you might expect request.post_vars.action and request.post_vars.arg. The "action" value will also be in one of the request headers. Not sure if you need or care about "arg".
A little step backward... I want to verify the call origin and authenticity.
Each time a call is performed by a webhook it is signed with a signature in the header obtained by encoding the body and I want to verify this signature in order to be sure from where the call comes from. I've found something similar for other languages and environments but not for python and web2py, for example this one https://stackoverflow.com/q/42182387/1039510. The concept is quite easy but there are some details I miss.
Hereunder I tryied to rewrite the example code[*] in a more clear way (I hope).
Does anybody tryied it before or somebody with some woocommerce webhook experience can point me to what's wrong in it?
def
compute(body):
secret = '<here is my secret key>'
dig = hmac.new(secret.encode(),
msg = body.encode(),
digestmod = hashlib.sha256
).digest()
computed = base64.b64encode(dig).decode()
return computed
def hookCheck(func):
def wrapper(*args, **kw):
signature = request.env.http_x_wc_webhook_signature
body = request.body.read() # ??
computed = compute(body)
if signature==computed:
return func(*args, **kw)
raise HTTP(403)
return wrapper
@service.json
def listenToHooks():
@hookCheck
def _main_():
# do stuff
return {}
return _main_()
Best regards
Manuele
[*]
https://gist.github.com/manuelep/4b64492ceeaa07f095302f94956ea554
def verify_signature():
secret = '<here is my secret key>'
body = request.body.read()
dig = hmac.new(secret.encode(), msg=body.encode(), digestmod=hashlib.sha256).digest()
if request.env.http_x_wc_webhook_signature != base64.b64encode(dig).decode():
raise HTTP(403)
@service.json
def listenToHooks():
verify_signature()
# do stuff
I think you're on the right track. If you need the original request body to verify the signature, request.body.read() should do it. Does that not work?Also, I don't think you need the decorator and nested function. Just write a simple function and call it at the beginning of the handler:
def verify_signature():
secret = '<here is my secret key>'
body = request.body.read()
dig = hmac.new(secret.encode(), msg=body.encode(), digestmod=hashlib.sha256).digest()
if request.env.http_x_wc_webhook_signature != base64.b64encode(dig).decode():
raise HTTP(403)
@service.json
def listenToHooks():
verify_signature()
# do stuffAnthony
def verify_signature(isinternal=True):
/dps
Don't you want a dummy parameter on verify_signature(), to prevent it being a URL-visible function?
On Wednesday, February 28, 2018 at 3:50:16 PM UTC-8, Anthony wrote:I think you're on the right track. If you need the original request body to verify the signature, request.body.read() should do it. Does that not work?Also, I don't think you need the decorator and nested function. Just write a simple function and call it at the beginning of the handler:
def verify_signature():
secret = '<here is my secret key>'
body = request.body.read()
dig = hmac.new(secret.encode(), msg=body.encode(), digestmod=hashlib.sha256).digest()
if request.env.http_x_wc_webhook_signature != base64.b64encode(dig).decode():
raise HTTP(403)
@service.json
def listenToHooks():
verify_signature()
# do stuffAnthony
Don't you want a dummy parameter on verify_signature(), to prevent it being a URL-visible function?
You might be better off getting help from folks who know WooCommerce, as this issue does not appear to be web2py specific.Yes for sure! Thanks a lot. M.
Before to definitely fly to other places where to find answers to my problem I have one little question related with web2py...
In woocommerce documentation they say this about request signature:
"X-WC-Webhook-Signature - a base64 encoded HMAC-SHA256 hash of the payload."[1]
Till now I interpreted "payload" as the request body... so as the json string I can read simply using `request.body.read()`.
Could it even be interpreted as the whole send content including the the request header?
How could it be get or reconstructed from the request storage object?
I'm not sure if it includes the entire original HTTP message or just the request body, but you can try request.env['wsgi.input']. If that doesn't work, web2py (and probably any WSGI-compliant framework) would not have access to the original HTTP message (which is parsed by the web server before passing request data to the web framework/application).
Anthony
Thanks Anthony for your attention,
now I've solved... there was no problem in the procedure but in
the tested data I copied from web services (such as requestb.in or
directly from the woocommerce event log web page) that I didn't
notice they were converting string such as "€" into the
character €. That's why I didn't get the correct encoded string.
Directly using what I get from request.body.read() everything
worked fine.
Best regards
Manuele