I am building a Twitter bot in MicroPython to run on a NodeMCU ESP8266 board. MicroPython doesn't have support for OAuth 1.0 requests out of the box, so I had to roll my own. I've been following these write-ups to build my program:
When ever I send a POST request to send a tweet, I get the following error response: {"errors":[{"code":215,"message":"Bad Authentication data."}]}
.
I wrote a small wrapper class for the MicroPython urequests
module, called oauth_requests
import urequests as requests
class oauth_request:
@classmethod
def post(cls, url, params, key_ring):
""" Post method with OAuth 1.0
Args:
url (str): URL to send request to.
params (dict): Params to append to URL.
key_ring (dict): Dictionary with API keys.
Returns:
Response from POST request.
"""
auth_header = cls.__create_auth_header("POST", url, params, **key_ring)
headers = {"Authorization": auth_header}
url += "?{}".format(
"&".join([
"{}={}".format(cls.__percent_encode(str(k)), cls.__percent_encode(str(v)))
for k, v in params.items()
]))
return requests.post(url, headers=headers)
The return value of cls.__create_auth_header(...)
is an "OAuth" string, like the one at the end of link #1 above. I have validated that my implementation of the HMAC-SHA1
algorithm produces the same output from the sample data in link #2 up above. I was able to send the same response through PostMan, so my API keys are valid.
Am I incorrectly creating the request headers correctly?
I have committed my code to this repo.
I ended up finding the fix for my problem. The main problem was I wasn't percent encoding my value for oauth_signature
. However, even after that I was getting a new error, {"errors":[{"code":32,"message":"Could not authenticate you."}]}
.
From the link I originally posted up above about creating the signature, you build the base parameter string from percent-encoding, and "&" joining an oauth
dictionary like this:
url_params = {
"status": "Tweeting from the future."
}
oauth = {
"include_entities": "true",
"oauth_consumer_key": consumer_key,
"oauth_nonce": generate_nonce(),
"oauth_signature_method": "HMAC-SHA1",
"oauth_timestamp": 946684800 + time.time(),
"oauth_token": access_token,
"oauth_version": 1.0,
}
oauth.update(url_params)
base_string = percent_encode_and_join(oauth)
(The time value is odd because the micropython system time epoch starts in Jan 1st, 2000 instead of Jan 1st, 1970)
However when I was debugging the request using PostMan it was working. I realized that Postman couldn't know to add that include_entities
entry when it is calculating the signature. Lo and behold when I removed that key from this dictionary, the error went away.
Refer to my repo up above for the code.