Repository: firebase/firebase-admin-python

Issue:
auth module should use the _http_client.JsonHttpClient
Currently the `auth` module uses the `AuthorizedHttpSession` directly. Instead it should use the `JsonHttpClient` like the other modules. This gives us a single point to control the HTTP interactions of the entire SDK.

Discussion:

Patch:
diff --git a/firebase_admin/_token_gen.py b/firebase_admin/_token_gen.py
index e979fb1..1ff9bc7 100644
--- a/firebase_admin/_token_gen.py
+++ b/firebase_admin/_token_gen.py
@@ -206,7 +206,7 @@ class TokenGenerator(object):
             'validDuration': expires_in,
         }
         try:
-            response = self.client.request('post', 'createSessionCookie', json=payload)
+            response = self.client.body('post', 'createSessionCookie', json=payload)
         except requests.exceptions.RequestException as error:
             self._handle_http_error(COOKIE_CREATE_ERROR, 'Failed to create session cookie', error)
         else:
diff --git a/firebase_admin/_user_mgt.py b/firebase_admin/_user_mgt.py
index 10a6398..7036a0b 100644
--- a/firebase_admin/_user_mgt.py
+++ b/firebase_admin/_user_mgt.py
@@ -394,7 +394,7 @@ class UserManager(object):
             raise TypeError('Unsupported keyword arguments: {0}.'.format(kwargs))
 
         try:
-            response = self._client.request('post', 'getAccountInfo', json=payload)
+            response = self._client.body('post', 'getAccountInfo', json=payload)
         except requests.exceptions.RequestException as error:
             msg = 'Failed to get user by {0}: {1}.'.format(key_type, key)
             self._handle_http_error(INTERNAL_ERROR, msg, error)
@@ -421,7 +421,7 @@ class UserManager(object):
         if page_token:
             payload['nextPageToken'] = page_token
         try:
-            return self._client.request('post', 'downloadAccount', json=payload)
+            return self._client.body('post', 'downloadAccount', json=payload)
         except requests.exceptions.RequestException as error:
             self._handle_http_error(USER_DOWNLOAD_ERROR, 'Failed to download user accounts.', error)
 
@@ -440,7 +440,7 @@ class UserManager(object):
         }
         payload = {k: v for k, v in payload.items() if v is not None}
         try:
-            response = self._client.request('post', 'signupNewUser', json=payload)
+            response = self._client.body('post', 'signupNewUser', json=payload)
         except requests.exceptions.RequestException as error:
             self._handle_http_error(USER_CREATE_ERROR, 'Failed to create new user.', error)
         else:
@@ -490,7 +490,7 @@ class UserManager(object):
 
         payload = {k: v for k, v in payload.items() if v is not None}
         try:
-            response = self._client.request('post', 'setAccountInfo', json=payload)
+            response = self._client.body('post', 'setAccountInfo', json=payload)
         except requests.exceptions.RequestException as error:
             self._handle_http_error(
                 USER_UPDATE_ERROR, 'Failed to update user: {0}.'.format(uid), error)
@@ -503,7 +503,7 @@ class UserManager(object):
         """Deletes the user identified by the specified user ID."""
         _auth_utils.validate_uid(uid, required=True)
         try:
-            response = self._client.request('post', 'deleteAccount', json={'localId' : uid})
+            response = self._client.body('post', 'deleteAccount', json={'localId' : uid})
         except requests.exceptions.RequestException as error:
             self._handle_http_error(
                 USER_DELETE_ERROR, 'Failed to delete user: {0}.'.format(uid), error)
@@ -529,7 +529,7 @@ class UserManager(object):
                 raise ValueError('A UserImportHash is required to import users with passwords.')
             payload.update(hash_alg.to_dict())
         try:
-            response = self._client.request('post', 'uploadAccount', json=payload)
+            response = self._client.body('post', 'uploadAccount', json=payload)
         except requests.exceptions.RequestException as error:
             self._handle_http_error(USER_IMPORT_ERROR, 'Failed to import users.', error)
         else:
diff --git a/firebase_admin/auth.py b/firebase_admin/auth.py
index dea0375..aa020b3 100644
--- a/firebase_admin/auth.py
+++ b/firebase_admin/auth.py
@@ -21,9 +21,8 @@ creating and managing user accounts in Firebase projects.
 
 import time
 
-from google.auth import transport
-
 import firebase_admin
+from firebase_admin import _http_client
 from firebase_admin import _token_gen
 from firebase_admin import _user_import
 from firebase_admin import _user_mgt
@@ -467,8 +466,14 @@ class AuthError(Exception):
 class _AuthService(object):
     """Firebase Authentication service."""
 
+    ID_TOOLKIT_URL = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/'
+
     def __init__(self, app):
-        client = _AuthHTTPClient(app)
+        credential = app.credential.get_credential()
+        version_header = 'Python/Admin/{0}'.format(firebase_admin.__version__)
+        client = _http_client.JsonHttpClient(
+            credential=credential, base_url=self.ID_TOOLKIT_URL,
+            headers={'X-Client-Version': version_header})
         self._token_generator = _token_gen.TokenGenerator(app, client)
         self._token_verifier = _token_gen.TokenVerifier(app)
         self._user_manager = _user_mgt.UserManager(client)
@@ -484,32 +489,3 @@ class _AuthService(object):
     @property
     def user_manager(self):
         return self._user_manager
-
-
-class _AuthHTTPClient(object):
-    """An HTTP client for making REST calls to the identity toolkit service."""
-
-    ID_TOOLKIT_URL = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/'
-
-    def __init__(self, app):
-        g_credential = app.credential.get_credential()
-        session = transport.requests.AuthorizedSession(g_credential)
-        version_header = 'Python/Admin/{0}'.format(firebase_admin.__version__)
-        session.headers.update({'X-Client-Version': version_header})
-        self.session = session
-
-    def request(self, method, urlpath, **kwargs):
-        """Makes an HTTP call using the Python requests library.
-
-        Args:
-            method: HTTP method name as a string (e.g. get, post).
-            urlpath: URL path of the endpoint. This will be appended to the server's base URL.
-            kwargs: An additional set of keyword arguments to be passed into requests API
-              (e.g. json, params).
-
-        Returns:
-            dict: The parsed JSON response.
-        """
-        resp = self.session.request(method, self.ID_TOOLKIT_URL + urlpath, **kwargs)
-        resp.raise_for_status()
-        return resp.json()


Test patch:
diff --git a/tests/test_token_gen.py b/tests/test_token_gen.py
index 1108b81..c5b686c 100644
--- a/tests/test_token_gen.py
+++ b/tests/test_token_gen.py
@@ -104,7 +104,7 @@ def _instrument_user_manager(app, status, payload):
     user_manager = auth_service.user_manager
     recorder = []
     user_manager._client.session.mount(
-        auth._AuthHTTPClient.ID_TOOLKIT_URL,
+        auth._AuthService.ID_TOOLKIT_URL,
         testutils.MockAdapter(payload, status, recorder))
     return user_manager, recorder
 
diff --git a/tests/test_user_mgt.py b/tests/test_user_mgt.py
index d9c851e..186748d 100644
--- a/tests/test_user_mgt.py
+++ b/tests/test_user_mgt.py
@@ -48,7 +48,7 @@ def _instrument_user_manager(app, status, payload):
     user_manager = auth_service.user_manager
     recorder = []
     user_manager._client.session.mount(
-        auth._AuthHTTPClient.ID_TOOLKIT_URL,
+        auth._AuthService.ID_TOOLKIT_URL,
         testutils.MockAdapter(payload, status, recorder))
     return user_manager, recorder