pythontorbridge

Requesting obfs4 bridges using the /moat/fetch interface via bridges.torproject.org in Python does not work


I have implemented the following Python function according to the documentation https://github.com/tladesignz/bridgedb?tab=readme-ov-file#accessing-the-moat-interface:

import requests

MOAT_SERVER_URL = "https://bridges.torproject.org"

def request_bridges():
    transports = ["obfs4"]
    payload = {
        "data": [{
            "version": "0.1.0",
            "type": "client-transports",
            "supported": transports,
        }]
    }

    headers = {
        "Content-Type": "application/json"
    }

    response = requests.post(f"{MOAT_SERVER_URL}/moat/fetch", json=payload, headers=headers)

    if response.status_code == 200:
        json_data = response.json()
        moat_data = json_data["data"][0] # <- Error
        if "challenge" in moat_data:
            # captcha_challenge(moat_data)
        else:
            pass # FIXME: Error Handling
    else:
        pass # FIXME: Error Handling

But the request does not seem to be correct because json_data has the following value:

{
   "errors":[
      {
         "id":"0",
         "type":"",
         "version":"0.1.0",
         "code":415,
         "status":"Unsupported Media Type",
         "detail":""
      }
   ]
}

The code should request bridges for the user via BridgeDB, so that the user can then solve the captcha which should be present in moat_data. (Just like the Tor Browser does)

I have already tried to send the payload as data instead of json:

import requests

MOAT_SERVER_URL = "https://bridges.torproject.org"

def request_bridges():
    transports = ["obfs4"]
    payload = {
        "data": [{
            "version": "0.1.0",
            "type": "client-transports",
            "supported": transports,
        }]
    }

    headers = {
        "Content-Type": "application/x-www-form-urlencoded"
    }

    response = requests.post(f"{MOAT_SERVER_URL}/moat/fetch", data=payload, headers=headers)
    # Rest of the code

I have also tried using Curl to check if the Python user agent might be blocked:

curl -X POST -H "Content-Type: application/json" -d '{"data": [{"version": "0.1.0", "type": "client-transports", "supported": ["obfs4"]}]}' https://bridges.torproject.org/moat/fetch

But this all returns the same JSON code as above.


Solution

  • As I found out myself, the BridgeDB uses the standard https://jsonapi.org/ which prescribes the content type "application/vnd.api+json".

    Here is the correct code for this function:

    import requests
    
    MOAT_SERVER_URL = "https://bridges.torproject.org"
    
    def request_bridges():
        transports = ["obfs4"]
        payload = {
            "data": [{
                "version": "0.1.0",
                "type": "client-transports",
                "supported": transports,
            }]
        }
    
        headers = {
            "Content-Type": "application/vnd.api+json"
        }
    
        response = requests.post(f"{MOAT_SERVER_URL}/moat/fetch", json=payload, headers=headers)
    
        if response.status_code == 200:
            json_data = response.json()
            moat_data = json_data["data"][0]
            if "challenge" in moat_data:
                # captcha_challenge(moat_data)
            else:
                pass # FIXME: Error Handling
        else:
            pass # FIXME: Error Handling