Add support for OAuth 2.0 Authorization Code Flow with Client methods
authorHarmon <Harmon758@gmail.com>
Thu, 24 Mar 2022 05:21:48 +0000 (00:21 -0500)
committerHarmon <Harmon758@gmail.com>
Thu, 24 Mar 2022 05:29:18 +0000 (00:29 -0500)
Add support for using OAuth 2.0 Authorization Code Flow with Client methods that require the authenticating user's ID
Raise TypeError for those methods when the access token isn't set

tweepy/client.py

index d83a464d85190b8faa9cb70e281b3a14b22ebd6a..ffd2c5a124441a3c37fa7fb3c661c1344e2f426b 100644 (file)
@@ -4,6 +4,13 @@
 
 from collections import namedtuple
 import datetime
+
+try:
+    from functools import cache
+except ImportError:  # Remove when support for Python 3.8 is dropped
+    from functools import lru_cache
+    cache = lru_cache(maxsize=None)
+
 import logging
 from platform import python_version
 import time
@@ -217,6 +224,45 @@ class Client(BaseClient):
         User agent used when making requests to the API
     """
 
+    def _get_authenticating_user_id(self, *, user_auth):
+        if user_auth:
+            if self.access_token is None:
+                raise TypeError(
+                    "Access Token must be provided for OAuth 1.0a User Context"
+                )
+            else:
+                return self._get_oauth_1_authenticating_user_id(
+                    self.access_token
+                )
+        else:
+            if self.bearer_token is None:
+                raise TypeError(
+                    "Access Token must be provided for "
+                    "OAuth 2.0 Authorization Code Flow with PKCE"
+                )
+            else:
+                return self._get_oauth_2_authenticating_user_id(
+                    self.bearer_token
+                )
+
+    @cache
+    def _get_oauth_1_authenticating_user_id(self, access_token):
+        return access_token.partition('-')[0]
+
+    @cache
+    def _get_oauth_2_authenticating_user_id(self, access_token):
+        original_access_token = self.bearer_token
+        original_return_type = self.return_type
+
+        self.bearer_token = access_token
+        self.return_type = dict
+        user_id = self.get_me(user_auth=False)["data"]["id"]
+
+        self.bearer_token = original_access_token
+        self.return_type = original_return_type
+
+        return user_id
+
     # Hide replies
 
     def hide_reply(self, id, *, user_auth=True):
@@ -281,9 +327,22 @@ class Client(BaseClient):
         The request succeeds with no action when the user sends a request to a
         user they're not liking the Tweet or have already unliked the Tweet.
 
+        .. note::
+
+            When using OAuth 2.0 Authorization Code Flow with PKCE with
+            ``user_auth=False``, a request is made beforehand to Twitter's API
+            to determine the authenticating user's ID. This is cached and only
+            done once per :class:`Client` instance for each access token used.
+
         .. versionchanged:: 4.5
             Added ``user_auth`` parameter
 
+        .. versionchanged:: 4.8
+            Added support for using OAuth 2.0 Authorization Code Flow with PKCE
+
+        .. versionchanged:: 4.8
+            Changed to raise :class:`TypeError` when the access token isn't set
+
         Parameters
         ----------
         tweet_id : int | str
@@ -291,6 +350,11 @@ class Client(BaseClient):
         user_auth : bool
             Whether or not to use OAuth 1.0a User Context to authenticate
 
+        Raises
+        ------
+        TypeError
+            If the access token isn't set
+
         Returns
         -------
         dict | requests.Response | Response
@@ -299,7 +363,7 @@ class Client(BaseClient):
         ----------
         https://developer.twitter.com/en/docs/twitter-api/tweets/likes/api-reference/delete-users-id-likes-tweet_id
         """
-        id = self.access_token.partition('-')[0]
+        id = self._get_authenticating_user_id(user_auth=user_auth)
         route = f"/2/users/{id}/likes/{tweet_id}"
 
         return self._make_request(
@@ -427,9 +491,22 @@ class Client(BaseClient):
     def like(self, tweet_id, *, user_auth=True):
         """Like a Tweet.
 
+        .. note::
+
+            When using OAuth 2.0 Authorization Code Flow with PKCE with
+            ``user_auth=False``, a request is made beforehand to Twitter's API
+            to determine the authenticating user's ID. This is cached and only
+            done once per :class:`Client` instance for each access token used.
+
         .. versionchanged:: 4.5
             Added ``user_auth`` parameter
 
+        .. versionchanged:: 4.8
+            Added support for using OAuth 2.0 Authorization Code Flow with PKCE
+
+        .. versionchanged:: 4.8
+            Changed to raise :class:`TypeError` when the access token isn't set
+
         Parameters
         ----------
         tweet_id : int | str
@@ -437,6 +514,11 @@ class Client(BaseClient):
         user_auth : bool
             Whether or not to use OAuth 1.0a User Context to authenticate
 
+        Raises
+        ------
+        TypeError
+            If the access token isn't set
+
         Returns
         -------
         dict | requests.Response | Response
@@ -445,7 +527,7 @@ class Client(BaseClient):
         ----------
         https://developer.twitter.com/en/docs/twitter-api/tweets/likes/api-reference/post-users-id-likes
         """
-        id = self.access_token.partition('-')[0]
+        id = self._get_authenticating_user_id(user_auth=user_auth)
         route = f"/2/users/{id}/likes"
 
         return self._make_request(
@@ -670,9 +752,22 @@ class Client(BaseClient):
         user they're not Retweeting the Tweet or have already removed the
         Retweet of.
 
+        .. note::
+
+            When using OAuth 2.0 Authorization Code Flow with PKCE with
+            ``user_auth=False``, a request is made beforehand to Twitter's API
+            to determine the authenticating user's ID. This is cached and only
+            done once per :class:`Client` instance for each access token used.
+
         .. versionchanged:: 4.5
             Added ``user_auth`` parameter
 
+        .. versionchanged:: 4.8
+            Added support for using OAuth 2.0 Authorization Code Flow with PKCE
+
+        .. versionchanged:: 4.8
+            Changed to raise :class:`TypeError` when the access token isn't set
+
         Parameters
         ----------
         source_tweet_id : int | str
@@ -680,6 +775,11 @@ class Client(BaseClient):
         user_auth : bool
             Whether or not to use OAuth 1.0a User Context to authenticate
 
+        Raises
+        ------
+        TypeError
+            If the access token isn't set
+
         Returns
         -------
         dict | requests.Response | Response
@@ -688,7 +788,7 @@ class Client(BaseClient):
         ----------
         https://developer.twitter.com/en/docs/twitter-api/tweets/retweets/api-reference/delete-users-id-retweets-tweet_id
         """
-        id = self.access_token.partition('-')[0]
+        id = self._get_authenticating_user_id(user_auth=user_auth)
         route = f"/2/users/{id}/retweets/{source_tweet_id}"
 
         return self._make_request(
@@ -756,9 +856,22 @@ class Client(BaseClient):
     def retweet(self, tweet_id, *, user_auth=True):
         """Causes the user ID to Retweet the target Tweet.
 
+        .. note::
+
+            When using OAuth 2.0 Authorization Code Flow with PKCE with
+            ``user_auth=False``, a request is made beforehand to Twitter's API
+            to determine the authenticating user's ID. This is cached and only
+            done once per :class:`Client` instance for each access token used.
+
         .. versionchanged:: 4.5
             Added ``user_auth`` parameter
 
+        .. versionchanged:: 4.8
+            Added support for using OAuth 2.0 Authorization Code Flow with PKCE
+
+        .. versionchanged:: 4.8
+            Changed to raise :class:`TypeError` when the access token isn't set
+
         Parameters
         ----------
         tweet_id : int | str
@@ -766,6 +879,11 @@ class Client(BaseClient):
         user_auth : bool
             Whether or not to use OAuth 1.0a User Context to authenticate
 
+        Raises
+        ------
+        TypeError
+            If the access token isn't set
+
         Returns
         -------
         dict | requests.Response | Response
@@ -774,7 +892,7 @@ class Client(BaseClient):
         ----------
         https://developer.twitter.com/en/docs/twitter-api/tweets/retweets/api-reference/post-users-id-retweets
         """
-        id = self.access_token.partition('-')[0]
+        id = self._get_authenticating_user_id(user_auth=user_auth)
         route = f"/2/users/{id}/retweets"
 
         return self._make_request(
@@ -1446,9 +1564,22 @@ class Client(BaseClient):
         The request succeeds with no action when the user sends a request to a
         user they're not blocking or have already unblocked.
 
+        .. note::
+
+            When using OAuth 2.0 Authorization Code Flow with PKCE with
+            ``user_auth=False``, a request is made beforehand to Twitter's API
+            to determine the authenticating user's ID. This is cached and only
+            done once per :class:`Client` instance for each access token used.
+
         .. versionchanged:: 4.5
             Added ``user_auth`` parameter
 
+        .. versionchanged:: 4.8
+            Added support for using OAuth 2.0 Authorization Code Flow with PKCE
+
+        .. versionchanged:: 4.8
+            Changed to raise :class:`TypeError` when the access token isn't set
+
         Parameters
         ----------
         target_user_id : int | str
@@ -1456,6 +1587,11 @@ class Client(BaseClient):
         user_auth : bool
             Whether or not to use OAuth 1.0a User Context to authenticate
 
+        Raises
+        ------
+        TypeError
+            If the access token isn't set
+
         Returns
         -------
         dict | requests.Response | Response
@@ -1464,7 +1600,7 @@ class Client(BaseClient):
         ----------
         https://developer.twitter.com/en/docs/twitter-api/users/blocks/api-reference/delete-users-user_id-blocking
         """
-        source_user_id = self.access_token.partition('-')[0]
+        source_user_id = self._get_authenticating_user_id(user_auth=user_auth)
         route = f"/2/users/{source_user_id}/blocking/{target_user_id}"
 
         return self._make_request(
@@ -1479,9 +1615,22 @@ class Client(BaseClient):
 
         Returns a list of users who are blocked by the authenticating user.
 
+        .. note::
+
+            When using OAuth 2.0 Authorization Code Flow with PKCE with
+            ``user_auth=False``, a request is made beforehand to Twitter's API
+            to determine the authenticating user's ID. This is cached and only
+            done once per :class:`Client` instance for each access token used.
+
         .. versionchanged:: 4.5
             Added ``user_auth`` parameter
 
+        .. versionchanged:: 4.8
+            Added support for using OAuth 2.0 Authorization Code Flow with PKCE
+
+        .. versionchanged:: 4.8
+            Changed to raise :class:`TypeError` when the access token isn't set
+
         Parameters
         ----------
         expansions : list[str] | str | None
@@ -1501,6 +1650,11 @@ class Client(BaseClient):
         user_auth : bool
             Whether or not to use OAuth 1.0a User Context to authenticate
 
+        Raises
+        ------
+        TypeError
+            If the access token isn't set
+
         Returns
         -------
         dict | requests.Response | Response
@@ -1509,7 +1663,7 @@ class Client(BaseClient):
         ----------
         https://developer.twitter.com/en/docs/twitter-api/users/blocks/api-reference/get-users-blocking
         """
-        id = self.access_token.partition('-')[0]
+        id = self._get_authenticating_user_id(user_auth=user_auth)
         route = f"/2/users/{id}/blocking"
 
         return self._make_request(
@@ -1523,9 +1677,22 @@ class Client(BaseClient):
     def block(self, target_user_id, *, user_auth=True):
         """Block another user.
 
+        .. note::
+
+            When using OAuth 2.0 Authorization Code Flow with PKCE with
+            ``user_auth=False``, a request is made beforehand to Twitter's API
+            to determine the authenticating user's ID. This is cached and only
+            done once per :class:`Client` instance for each access token used.
+
         .. versionchanged:: 4.5
             Added ``user_auth`` parameter
 
+        .. versionchanged:: 4.8
+            Added support for using OAuth 2.0 Authorization Code Flow with PKCE
+
+        .. versionchanged:: 4.8
+            Changed to raise :class:`TypeError` when the access token isn't set
+
         Parameters
         ----------
         target_user_id : int | str
@@ -1533,6 +1700,11 @@ class Client(BaseClient):
         user_auth : bool
             Whether or not to use OAuth 1.0a User Context to authenticate
 
+        Raises
+        ------
+        TypeError
+            If the access token isn't set
+
         Returns
         -------
         dict | requests.Response | Response
@@ -1541,7 +1713,7 @@ class Client(BaseClient):
         ----------
         https://developer.twitter.com/en/docs/twitter-api/users/blocks/api-reference/post-users-user_id-blocking
         """
-        id = self.access_token.partition('-')[0]
+        id = self._get_authenticating_user_id(user_auth=user_auth)
         route = f"/2/users/{id}/blocking"
 
         return self._make_request(
@@ -1557,12 +1729,25 @@ class Client(BaseClient):
         The request succeeds with no action when the authenticated user sends a
         request to a user they're not following or have already unfollowed.
 
+        .. note::
+
+            When using OAuth 2.0 Authorization Code Flow with PKCE with
+            ``user_auth=False``, a request is made beforehand to Twitter's API
+            to determine the authenticating user's ID. This is cached and only
+            done once per :class:`Client` instance for each access token used.
+
         .. versionchanged:: 4.2
             Renamed from :meth:`Client.unfollow`
 
         .. versionchanged:: 4.5
             Added ``user_auth`` parameter
 
+        .. versionchanged:: 4.8
+            Added support for using OAuth 2.0 Authorization Code Flow with PKCE
+
+        .. versionchanged:: 4.8
+            Changed to raise :class:`TypeError` when the access token isn't set
+
         Parameters
         ----------
         target_user_id : int | str
@@ -1570,6 +1755,11 @@ class Client(BaseClient):
         user_auth : bool
             Whether or not to use OAuth 1.0a User Context to authenticate
 
+        Raises
+        ------
+        TypeError
+            If the access token isn't set
+
         Returns
         -------
         dict | requests.Response | Response
@@ -1578,7 +1768,7 @@ class Client(BaseClient):
         ----------
         https://developer.twitter.com/en/docs/twitter-api/users/follows/api-reference/delete-users-source_id-following
         """
-        source_user_id = self.access_token.partition('-')[0]
+        source_user_id = self._get_authenticating_user_id(user_auth=user_auth)
         route = f"/2/users/{source_user_id}/following/{target_user_id}"
 
         return self._make_request(
@@ -1702,12 +1892,25 @@ class Client(BaseClient):
         request to a user they're already following, or if they're sending a
         follower request to a user that does not have public Tweets.
 
+        .. note::
+
+            When using OAuth 2.0 Authorization Code Flow with PKCE with
+            ``user_auth=False``, a request is made beforehand to Twitter's API
+            to determine the authenticating user's ID. This is cached and only
+            done once per :class:`Client` instance for each access token used.
+
         .. versionchanged:: 4.2
             Renamed from :meth:`Client.follow`
 
         .. versionchanged:: 4.5
             Added ``user_auth`` parameter
 
+        .. versionchanged:: 4.8
+            Added support for using OAuth 2.0 Authorization Code Flow with PKCE
+
+        .. versionchanged:: 4.8
+            Changed to raise :class:`TypeError` when the access token isn't set
+
         Parameters
         ----------
         target_user_id : int | str
@@ -1715,6 +1918,11 @@ class Client(BaseClient):
         user_auth : bool
             Whether or not to use OAuth 1.0a User Context to authenticate
 
+        Raises
+        ------
+        TypeError
+            If the access token isn't set
+
         Returns
         -------
         dict | requests.Response | Response
@@ -1723,7 +1931,7 @@ class Client(BaseClient):
         ----------
         https://developer.twitter.com/en/docs/twitter-api/users/follows/api-reference/post-users-source_user_id-following
         """
-        source_user_id = self.access_token.partition('-')[0]
+        source_user_id = self._get_authenticating_user_id(user_auth=user_auth)
         route = f"/2/users/{source_user_id}/following"
 
         return self._make_request(
@@ -1751,9 +1959,22 @@ class Client(BaseClient):
         The request succeeds with no action when the user sends a request to a
         user they're not muting or have already unmuted.
 
+        .. note::
+
+            When using OAuth 2.0 Authorization Code Flow with PKCE with
+            ``user_auth=False``, a request is made beforehand to Twitter's API
+            to determine the authenticating user's ID. This is cached and only
+            done once per :class:`Client` instance for each access token used.
+
         .. versionchanged:: 4.5
             Added ``user_auth`` parameter
 
+        .. versionchanged:: 4.8
+            Added support for using OAuth 2.0 Authorization Code Flow with PKCE
+
+        .. versionchanged:: 4.8
+            Changed to raise :class:`TypeError` when the access token isn't set
+
         Parameters
         ----------
         target_user_id : int | str
@@ -1761,6 +1982,11 @@ class Client(BaseClient):
         user_auth : bool
             Whether or not to use OAuth 1.0a User Context to authenticate
 
+        Raises
+        ------
+        TypeError
+            If the access token isn't set
+
         Returns
         -------
         dict | requests.Response | Response
@@ -1769,7 +1995,7 @@ class Client(BaseClient):
         ----------
         https://developer.twitter.com/en/docs/twitter-api/users/mutes/api-reference/delete-users-user_id-muting
         """
-        source_user_id = self.access_token.partition('-')[0]
+        source_user_id = self._get_authenticating_user_id(user_auth=user_auth)
         route = f"/2/users/{source_user_id}/muting/{target_user_id}"
 
         return self._make_request(
@@ -1784,11 +2010,24 @@ class Client(BaseClient):
 
         Returns a list of users who are muted by the authenticating user.
 
+        .. note::
+
+            When using OAuth 2.0 Authorization Code Flow with PKCE with
+            ``user_auth=False``, a request is made beforehand to Twitter's API
+            to determine the authenticating user's ID. This is cached and only
+            done once per :class:`Client` instance for each access token used.
+
         .. versionadded:: 4.1
 
         .. versionchanged:: 4.5
             Added ``user_auth`` parameter
 
+        .. versionchanged:: 4.8
+            Added support for using OAuth 2.0 Authorization Code Flow with PKCE
+
+        .. versionchanged:: 4.8
+            Changed to raise :class:`TypeError` when the access token isn't set
+
         Parameters
         ----------
         expansions : list[str] | str | None
@@ -1808,6 +2047,11 @@ class Client(BaseClient):
         user_auth : bool
             Whether or not to use OAuth 1.0a User Context to authenticate
 
+        Raises
+        ------
+        TypeError
+            If the access token isn't set
+
         Returns
         -------
         dict | requests.Response | Response
@@ -1816,7 +2060,7 @@ class Client(BaseClient):
         ----------
         https://developer.twitter.com/en/docs/twitter-api/users/mutes/api-reference/get-users-muting
         """
-        id = self.access_token.partition('-')[0]
+        id = self._get_authenticating_user_id(user_auth=user_auth)
         route = f"/2/users/{id}/muting"
 
         return self._make_request(
@@ -1830,9 +2074,22 @@ class Client(BaseClient):
     def mute(self, target_user_id, *, user_auth=True):
         """Allows an authenticated user ID to mute the target user.
 
+        .. note::
+
+            When using OAuth 2.0 Authorization Code Flow with PKCE with
+            ``user_auth=False``, a request is made beforehand to Twitter's API
+            to determine the authenticating user's ID. This is cached and only
+            done once per :class:`Client` instance for each access token used.
+
         .. versionchanged:: 4.5
             Added ``user_auth`` parameter
 
+        .. versionchanged:: 4.8
+            Added support for using OAuth 2.0 Authorization Code Flow with PKCE
+
+        .. versionchanged:: 4.8
+            Changed to raise :class:`TypeError` when the access token isn't set
+
         Parameters
         ----------
         target_user_id : int | str
@@ -1840,6 +2097,11 @@ class Client(BaseClient):
         user_auth : bool
             Whether or not to use OAuth 1.0a User Context to authenticate
 
+        Raises
+        ------
+        TypeError
+            If the access token isn't set
+
         Returns
         -------
         dict | requests.Response | Response
@@ -1848,7 +2110,7 @@ class Client(BaseClient):
         ----------
         https://developer.twitter.com/en/docs/twitter-api/users/mutes/api-reference/post-users-user_id-muting
         """
-        id = self.access_token.partition('-')[0]
+        id = self._get_authenticating_user_id(user_auth=user_auth)
         route = f"/2/users/{id}/muting"
 
         return self._make_request(
@@ -2295,11 +2557,24 @@ class Client(BaseClient):
     def unfollow_list(self, list_id, *, user_auth=True):
         """Enables the authenticated user to unfollow a List.
 
+        .. note::
+
+            When using OAuth 2.0 Authorization Code Flow with PKCE with
+            ``user_auth=False``, a request is made beforehand to Twitter's API
+            to determine the authenticating user's ID. This is cached and only
+            done once per :class:`Client` instance for each access token used.
+
         .. versionadded:: 4.2
 
         .. versionchanged:: 4.5
             Added ``user_auth`` parameter
 
+        .. versionchanged:: 4.8
+            Added support for using OAuth 2.0 Authorization Code Flow with PKCE
+
+        .. versionchanged:: 4.8
+            Changed to raise :class:`TypeError` when the access token isn't set
+
         Parameters
         ----------
         list_id : int | str
@@ -2307,6 +2582,11 @@ class Client(BaseClient):
         user_auth : bool
             Whether or not to use OAuth 1.0a User Context to authenticate
 
+        Raises
+        ------
+        TypeError
+            If the access token isn't set
+
         Returns
         -------
         dict | requests.Response | Response
@@ -2315,7 +2595,7 @@ class Client(BaseClient):
         ----------
         https://developer.twitter.com/en/docs/twitter-api/lists/list-follows/api-reference/delete-users-id-followed-lists-list_id
         """
-        id = self.access_token.partition('-')[0]
+        id = self._get_authenticating_user_id(user_auth=user_auth)
         route = f"/2/users/{id}/followed_lists/{list_id}"
 
         return self._make_request(
@@ -2423,11 +2703,24 @@ class Client(BaseClient):
     def follow_list(self, list_id, *, user_auth=True):
         """Enables the authenticated user to follow a List.
 
+        .. note::
+
+            When using OAuth 2.0 Authorization Code Flow with PKCE with
+            ``user_auth=False``, a request is made beforehand to Twitter's API
+            to determine the authenticating user's ID. This is cached and only
+            done once per :class:`Client` instance for each access token used.
+
         .. versionadded:: 4.2
 
         .. versionchanged:: 4.5
             Added ``user_auth`` parameter
 
+        .. versionchanged:: 4.8
+            Added support for using OAuth 2.0 Authorization Code Flow with PKCE
+
+        .. versionchanged:: 4.8
+            Changed to raise :class:`TypeError` when the access token isn't set
+
         Parameters
         ----------
         list_id : int | str
@@ -2435,6 +2728,11 @@ class Client(BaseClient):
         user_auth : bool
             Whether or not to use OAuth 1.0a User Context to authenticate
 
+        Raises
+        ------
+        TypeError
+            If the access token isn't set
+
         Returns
         -------
         dict | requests.Response | Response
@@ -2443,7 +2741,7 @@ class Client(BaseClient):
         ----------
         https://developer.twitter.com/en/docs/twitter-api/lists/list-follows/api-reference/post-users-id-followed-lists
         """
-        id = self.access_token.partition('-')[0]
+        id = self._get_authenticating_user_id(user_auth=user_auth)
         route = f"/2/users/{id}/followed_lists"
 
         return self._make_request(
@@ -2819,11 +3117,24 @@ class Client(BaseClient):
     def unpin_list(self, list_id, *, user_auth=True):
         """Enables the authenticated user to unpin a List.
 
+        .. note::
+
+            When using OAuth 2.0 Authorization Code Flow with PKCE with
+            ``user_auth=False``, a request is made beforehand to Twitter's API
+            to determine the authenticating user's ID. This is cached and only
+            done once per :class:`Client` instance for each access token used.
+
         .. versionadded:: 4.2
 
         .. versionchanged:: 4.5
             Added ``user_auth`` parameter
 
+        .. versionchanged:: 4.8
+            Added support for using OAuth 2.0 Authorization Code Flow with PKCE
+
+        .. versionchanged:: 4.8
+            Changed to raise :class:`TypeError` when the access token isn't set
+
         Parameters
         ----------
         list_id : int | str
@@ -2831,6 +3142,11 @@ class Client(BaseClient):
         user_auth : bool
             Whether or not to use OAuth 1.0a User Context to authenticate
 
+        Raises
+        ------
+        TypeError
+            If the access token isn't set
+
         Returns
         -------
         dict | requests.Response | Response
@@ -2839,7 +3155,7 @@ class Client(BaseClient):
         ----------
         https://developer.twitter.com/en/docs/twitter-api/lists/pinned-lists/api-reference/delete-users-id-pinned-lists-list_id
         """
-        id = self.access_token.partition('-')[0]
+        id = self._get_authenticating_user_id(user_auth=user_auth)
         route = f"/2/users/{id}/pinned_lists/{list_id}"
 
         return self._make_request(
@@ -2852,11 +3168,24 @@ class Client(BaseClient):
 
         Returns the Lists pinned by a specified user.
 
+        .. note::
+
+            When using OAuth 2.0 Authorization Code Flow with PKCE with
+            ``user_auth=False``, a request is made beforehand to Twitter's API
+            to determine the authenticating user's ID. This is cached and only
+            done once per :class:`Client` instance for each access token used.
+
         .. versionadded:: 4.4
 
         .. versionchanged:: 4.5
             Added ``user_auth`` parameter
 
+        .. versionchanged:: 4.8
+            Added support for using OAuth 2.0 Authorization Code Flow with PKCE
+
+        .. versionchanged:: 4.8
+            Changed to raise :class:`TypeError` when the access token isn't set
+
         Parameters
         ----------
         expansions : list[str] | str | None
@@ -2868,6 +3197,11 @@ class Client(BaseClient):
         user_auth : bool
             Whether or not to use OAuth 1.0a User Context to authenticate
 
+        Raises
+        ------
+        TypeError
+            If the access token isn't set
+
         Returns
         -------
         dict | requests.Response | Response
@@ -2876,7 +3210,7 @@ class Client(BaseClient):
         ----------
         https://developer.twitter.com/en/docs/twitter-api/lists/pinned-lists/api-reference/get-users-id-pinned_lists
         """
-        id = self.access_token.partition('-')[0]
+        id = self._get_authenticating_user_id(user_auth=user_auth)
         route = f"/2/users/{id}/pinned_lists"
 
         return self._make_request(
@@ -2889,11 +3223,24 @@ class Client(BaseClient):
     def pin_list(self, list_id, *, user_auth=True):
         """Enables the authenticated user to pin a List.
 
+        .. note::
+
+            When using OAuth 2.0 Authorization Code Flow with PKCE with
+            ``user_auth=False``, a request is made beforehand to Twitter's API
+            to determine the authenticating user's ID. This is cached and only
+            done once per :class:`Client` instance for each access token used.
+
         .. versionadded:: 4.2
 
         .. versionchanged:: 4.5
             Added ``user_auth`` parameter
 
+        .. versionchanged:: 4.8
+            Added support for using OAuth 2.0 Authorization Code Flow with PKCE
+
+        .. versionchanged:: 4.8
+            Changed to raise :class:`TypeError` when the access token isn't set
+
         Parameters
         ----------
         list_id : int | str
@@ -2901,6 +3248,11 @@ class Client(BaseClient):
         user_auth : bool
             Whether or not to use OAuth 1.0a User Context to authenticate
 
+        Raises
+        ------
+        TypeError
+            If the access token isn't set
+
         Returns
         -------
         dict | requests.Response | Response
@@ -2909,7 +3261,7 @@ class Client(BaseClient):
         ----------
         https://developer.twitter.com/en/docs/twitter-api/lists/pinned-lists/api-reference/post-users-id-pinned-lists
         """
-        id = self.access_token.partition('-')[0]
+        id = self._get_authenticating_user_id(user_auth=user_auth)
         route = f"/2/users/{id}/pinned_lists"
 
         return self._make_request(