I am trying to set up a “bot” account (separate from my own) that can be automated. I have been successful at accessing and tweeting from this account, but I’m running into authorization/authentication errors once the access token has expired.

From what I understand, once you have initially granted access to another account, all I should have to do is manage the generation and handling of the refresh token. – For simplicity’s sake, here is my process:

  1. Check for a “twitter_token.txt” file – this file stores the token dictionary in JSON format that is retrieved from the fetch_token() method.

  2. If the file doesn’t exist: the token needs to be created for the first time (step N/A for this scenario)

  3. If the file does exist: load the token from the JSON file, and convert back into a dict object

  4. I create a new OAuth2Session using the client_id and token_data as input

  5. I send a POST request to (https)://api.twitter.com/2/tweets to create a new tweet – if the token is expired, it attempts to call refresh_token.

    # Extra credentials to be passed along when refreshing tokens, usually for authentication purposes.
    extra = {
        'client_id': client_id,
        'client_secret': client_secret,
    }

    headers = {
        "Authorization": "Bearer {}".format(token_data["access_token"]),
        "Content-Type": "application/json"
    }

    try:
        client = OAuth2Session(client_id, token=token_data)
        r = client.post('https://api.twitter.com/2/tweets', json={'text': 'Testing 1, 2, 3, 4, 5'}, headers=headers)
    except TokenExpiredError as e:
        token_data = client.refresh_token(refresh_url, **extra)
        token_saver(token_data)
        client = OAuth2Session(client_id, token=token_data)
        r = client.post('https://api.twitter.com/2/tweets', json={'text': 'Testing 1, 2, 3, 4, 5'}, headers=headers)

    print(r.content)
    print("")

The error happens once it gets to: token_data = client.refresh_token(refresh_url, **extra)

and I get the following error:

Can anyone explain to me why I’m getting this error? From what I’ve read, as long as I have the access token, as long as I refresh it when it expires, I should still be able to access the bot account.

I ended up figuring out a solution that worked for me. In case anyone stumbles across this moving forward, here was the fix:

When refreshing your access_token, you need to ensure that you have a proper “authorization header” (something I did not create, previously). – Along with the refresh_url and data, the request I formed looks something like this:

url = 'https://api.twitter.com/2/oauth2/token'
authorization = create_authorization(client_id, client_secret)
auth_headers = {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Authorization': authorization
}

data = {
    'refresh_token': token_data["refresh_token"],
    'grant_type': 'refresh_token'
}

For the authorization parameter I created a method that, according to the Working with confidential clients section of OAuth 2.0 Making requests on behalf of users, uses

a basic authentication scheme for generating an authorization header with base64 encoding while making requests to the token endpoints.

The create_authorization method looks like this:

def create_authorization(c_id, c_secret):
    # Concatenate the consumer key and secret, separated by a colon
    credentials = f'{c_id}:{c_secret}'

    # Encode the credentials using base64
    encoded_credentials = base64.b64encode(credentials.encode('utf-8')).decode('utf-8')

    # Construct the authorization header
    authorization_header = f'Basic {encoded_credentials}'
    return authorization_header

Then you can just make a POST request to url = 'https://api.twitter.com/2/oauth2/token'
and you will receive your new token:

response = requests.post(url, headers=auth_headers, data=data)
token_data = response.json()

Thanks for following along (hopefully)!

1 Like