Conor Romano
Conor Romano

Reputation: 150

Tweepy .get_me() error when using OAuth 2.0 User Auth flow with PKCE

I'm trying to get an authenticated user's Twitter ID and Twitter username via the Tweepy package. With authentication through the OAuth 2.0 flow (using a Django view). I've been able to create an instance of the Tweepy pkg's Client class using the OAuth 2.0 User Context flow, but when I use the .get_me() method on it, I get an error.

The error is: "Consumer key must be string or bytes, not NoneType"

The error would suggest that the method wants a Consumer Key, but in this case, I initialized the Class using the OAuth 2.0 User Context flow with PKCE, which doesn't require one.

I created the instance with the snippet below (in the same way it's specified in the Tweepy docs here):

import tweepy

oauth2_user_handler = tweepy.OAuth2UserHandler(
    client_id="Client ID here",
    redirect_uri="Callback / Redirect URI / URL here",
    scope=["follows.read", "users.read", "tweet.read", "offline.access"],
    
    client_secret="Client Secret here"
)

access_token = oauth2_user_handler.fetch_token(
    "Authorization Response URL here"
)

client = tweepy.Client("Access Token here")

The Client object doesn't appear to be the issue, as I'm getting redirected to Twitter to authorize and redirected back via the callback as it should work. The Consumer Key is a requirement of the OAuth 1.0 User Context flow, but that's not what I wanted to initialize with here.

Here is the get_me() method from the client.py file in the Tweepy pkg:

def get_me(self, *, user_auth=True, **params):
    """get_me(*, expansions=None, tweet_fields=None, user_fields=None, \
              user_auth=True)
    Returns information about an authorized user.
    .. versionadded:: 4.5
    Parameters
    ----------
    expansions : list[str] | str | None
        :ref:`expansions_parameter`
    tweet_fields : list[str] | str | None
        :ref:`tweet_fields_parameter`
    user_fields : list[str] | str | None
        :ref:`user_fields_parameter`
    user_auth : bool
        Whether or not to use OAuth 1.0a User Context to authenticate
    Returns
    -------
    dict | requests.Response | Response
    References
    ----------
    https://developer.twitter.com/en/docs/twitter-api/users/lookup/api-reference/get-users-me
    """
    return self._make_request(
        "GET", f"/2/users/me", params=params,
        endpoint_parameters=("expansions", "tweet.fields", "user.fields"),
        data_type=User, user_auth=user_auth
    )

# Search Spaces

I've tried passing through the OAuth 1.0 required tags (Bearer Token, Consumer Key, Consumer Secret, Access Token, and Access Secret) when building the Client instance to test, and this causes the method to work as it should. When testing this, I set Bearer Token to be the "access_token" above. This defeats the purpose though, as I will not have access_token and access_token_secret from external users (I was able to get both for my own account via the the Twitter Dev Portal).

Have also tried setting the user_auth parameter to false when calling the method, but this causes a 403 Forbidden error, "Authenticating with OAuth 2.0 Application-Only is forbidden for this endpoint. Supported authentication types are [OAuth 1.0a User Context, OAuth 2.0 User Context]."

Is the issue the method? Or am I missing something?

Tweepy documentation for the .get_me() method: https://docs.tweepy.org/en/stable/client.html#tweepy.Client.get_me

Source for the Client class & get_me() method in Tweepy repo: https://github.com/tweepy/tweepy/blob/master/tweepy/client.py

Twitter API documentation for GET /2/users/me: https://developer.twitter.com/en/docs/twitter-api/users/lookup/api-reference/get-users-me

Upvotes: 1

Views: 805

Answers (1)

Conor Romano
Conor Romano

Reputation: 150

This issue was solved for me by a Contributor to the project, Harmon758, here.

The solution required two things. First, setting the user_auth parameter to False when calling the .get_me() method. This must be done because the function defaults to OAuth 1.0 if not.

And then second, to get around the 401 unauthorized error mentioned in the comment, I needed to tweak the format of the Access Token that I was passing through to the Client object. I was trying to pass through 'access_token' in the above code, but it should have been 'access_token["access_token"]'. The return from the .fetch_token() method is a dictionary that contains the required field.

Correct code:

Build handler with:

oauth2_user_handler = tweepy.OAuth2UserHandler(
    client_id='your_client_id',
    redirect_uri="your_redirect_uri",
    scope=["tweet.read", "and any others you need"],
    # Client Secret is only necessary if using a confidential client
    client_secret='your_client_secret'
    )

Redirect to Twitter for authorization with:

def auth(request):

    return HttpResponseRedirect(oauth2_user_handler.get_authorization_url())

And finally, callback to:

def callback(request):

    access_token = oauth2_user_handler.fetch_token(request.build_absolute_uri())

    user_client = tweepy.Client(
        access_token["access_token"]
    )

    me = user_client.get_me(user_auth=False)

    return HttpResponse()

Upvotes: 2

Related Questions