Hi, I’m trying to implement sign in via Twitter button on my website using Python, following this flow: https://dev.twitter.com/web/sign-in/implementing.
The problem is that I’m getting 401 error no matter what upon the very first request to https://api.twitter.com/oauth/request_token endpoint.
I have the button “Allow this application to sign in with Twitter” enabled:
I also tried to debug this in OAuth Tool by getting a raw curl request from its signature-generator, but there seems to be a flaw in OAuth Tool, cause it would always include “oauth_token” parameter and calculate the signature with it, which is wrong, cause “request_token” endpoint shouldn’t accept this parameter.
So, how do I debug it?
Just in case, here is my source code for Django:
def twitter_oauth_submit_button_view(request):
TWITTER_CLIENT_SECRET = settings.TWITTER_CLIENT_SECRET
TWITTER_CLIENT_ID = settings.TWITTER_CLIENT_SECRET
urlencode = urllib.quote_plus
nonce = make_nonce()
timestamp = int(time.time())
# encode with client secret, NOTE the ampersand sign added to secret! Ampersand comes from the fact, that encoding key
# should normally be "TWITTER_CLIENT_SECRET&OAUTH_TOKEN_SECRET", but as we don't have OAUTH_TOKEN_SECRET yet, we should
# use just "TWITTER_CLIENT_SECRET&".
# also note the urlencode() around TWITTER_CLIENT_SECRET
hmaced = hmac.new(urlencode(TWITTER_CLIENT_SECRET)+"&", make_basestring(nonce=nonce, timestamp=timestamp), sha1)
signature = hmaced.digest().encode("base64").rstrip("\n")
oauth_header = ('OAuth ' +
'oauth_callback="' + urlencode('http://sabotage.toolly.ru/twitter_oauth2callback') + '", ' +
'oauth_consumer_key="' + urlencode(TWITTER_CLIENT_SECRET) + '", ' +
'oauth_nonce="' + urlencode(nonce) + '", ' +
'oauth_signature="' + urlencode(signature) + '", ' +
'oauth_signature_method="' + urlencode('HMAC-SHA1') + '", ' +
'oauth_timestamp="' + urlencode(str(timestamp)) + '", ' +
'oauth_version="1.0"')
headers = {'Authorization': oauth_header}
r = requests.post("https://api.twitter.com/oauth/request_token", headers=headers, data={})
return HttpResponseRedirect("https://api.twitter.com/oauth/authenticate?oauth_token=%s" % r.data["auth_token"])
def make_basestring(nonce, timestamp, request_type="POST", url="https://api.twitter.com/oauth/request_token"):
urlencode = urllib.quote_plus
params = [("oauth_callback", 'http://sabotage.toolly.ru/twitter_oauth2callback'),
("oauth_consumer_key", settings.TWITTER_CLIENT_ID),
("oauth_nonce", nonce),
("oauth_signature_method", "HMAC-SHA1"),
("oauth_timestamp", str(timestamp)),
("oauth_version", "1.0")]
oauth_params_string = "&".join([(urlencode(key) + "=" + urlencode(value)) for key, value in params])
base_string = urlencode(request_type) + "&" + urlencode(url) + "&" + urlencode(oauth_params_string)
return base_string
def make_nonce(length=32, charset='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'):
return "".join([choice(charset) for i in range(length)])
My basestring is:
POST&https%3A%2F%2Fapi.twitter.com%2Foauth%2Frequest_token&oauth_callback%3Dhttp%253A%252F%252Fsabotage.toolly.ru%252Ftwitter_oauth2callback%26oauth_consumer_key%3DmBDDk6yXAX5EbF1R3NLwAXPRe%26oauth_nonce%3D6EzEITZAfuVEPaVjIBMK3GV54K18Gk3k%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1417604435%26oauth_version%3D1.0
My Authentication header is:
OAuth oauth_callback="http%3A%2F%2Fsabotage.toolly.ru%2Ftwitter_oauth2callback", oauth_consumer_key="7HoVWmsICqmRAGQJCym4OESn6qbhZm9XCvg9AjRcVKM3MG71s1", oauth_nonce="6EzEITZAfuVEPaVjIBMK3GV54K18Gk3k", oauth_signature="JLLLrJ75SUsfxjTqBsIQCGaZkRM%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1417604435", oauth_version="1.0"