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 ( 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.

"" (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: