Sunday, January 13, 2019

Python3: Authenticate with Discord API using urllib.request

I wanted to do some automation with Discord, so I started writing a Python script to query Discord's API. First step is to authenticate with Discord. Following the documentation, I sent a POST request to the token URL. However, I was getting a HTTP 403 with error message
"Please enable cookies. Error 1010...Access denied...The owner of this website (discordapp.com) has banned your access based on your browser's signature..." It appears to be protected by Cloudflare.

First thing I tried was to use http.cookiejar for my POST request, but that did not help. Following the sample code in the documentation, I switched over to requests instead of the built-in library urllib.request, and it worked! But I was curious why it did not work with urllib.request.

Then I tried Postman to send the POST request, and it worked as well. Postman has a code generation feature for http.client (Python 3). There were some errors in the generated code, but I was able to fix them and got it to work with Discord. http.client is a low-level library and used by urllib.request, so why is it working with http.client but not urllib.request?

Next, I fired up Wireshark to monitor and compare the POST requests. After bunch of trial and error, I found the answer to be rather simple.

User-Agent:
"" (empty string) - works
"python-requests/2.21.0" - works
"Python-urllib/3.6" - does not work

It turns out Cloudflare does not like the user-agent string from urllib.request. Requests from http.client do not have an user-agent set by default, thus it worked. So for anyone who want to use urllib.request, set the user-agent to blank or match with your browser should avoid this error.

No comments: