Repository: alexgolec/tda-api

Issue:
authentication failure
Hey

I've created an app on TD Ameritrade with a callback URL `https://127.0.0.1:8080` (as [suggested](https://developer.tdameritrade.com/content/authentication-faq) on developer pages). I am making sure I am sending the correct API_KEY and redirect_url to the authentication flow

```
with webdriver.Chrome() as driver:
    c = auth.client_from_login_flow(driver, apikey, redirect_uri, token_path)
```

However, I am getting the error page `a third party application` [you document](https://tda-api.readthedocs.io/en/latest/auth.html). 

Logging shows the port on the localhost being sent with the GET is different however

```
2020-06-08 16:48:47,541 DEBUG    GET http://127.0.0.1:57214/session/<SESSION>/url {}
2020-06-08 16:48:47,544 DEBUG    http://127.0.0.1:57214 "GET /session/<SESSION>/url HTTP/1.1" 200 189
2020-06-08 16:48:47,544 DEBUG    Finished Request
```

Comments:

Patch:
diff --git a/tda/auth.py b/tda/auth.py
index d9c7976..105dadb 100644
--- a/tda/auth.py
+++ b/tda/auth.py
@@ -58,6 +58,7 @@ def client_from_token_file(token_path, api_key):
     __register_token_redactions(token)
 
     # Return a new session configured to refresh credentials
+    api_key = __normalize_api_key(api_key)
     return Client(
         api_key,
         OAuth2Session(api_key, token=token,
@@ -89,6 +90,8 @@ def client_from_login_flow(webdriver, api_key, redirect_url, token_path,
     get_logger().info(('Creating new token with redirect URL \'{}\' ' +
                        'and token path \'{}\'').format(redirect_url, token_path))
 
+    api_key = __normalize_api_key(api_key)
+
     oauth = OAuth2Session(api_key, redirect_uri=redirect_url)
     authorization_url, state = oauth.authorization_url(
         'https://auth.tdameritrade.com/auth')
@@ -155,7 +158,6 @@ def easy_client(api_key, redirect_uri, token_path, webdriver_func=None):
                            a new token. Will only be called if the token file
                            cannot be found.
     '''
-    api_key = __normalize_api_key(api_key)
     logger = get_logger()
 
     try:


Test patch:
diff --git a/tests/auth_test.py b/tests/auth_test.py
index d9457ed..ebf9d43 100644
--- a/tests/auth_test.py
+++ b/tests/auth_test.py
@@ -9,7 +9,7 @@ import tempfile
 import unittest
 
 
-API_KEY = 'APIKEY'
+API_KEY = 'APIKEY@AMER.OAUTHAP'
 
 
 class ClientFromTokenFileTest(unittest.TestCase):
@@ -46,6 +46,24 @@ class ClientFromTokenFileTest(unittest.TestCase):
             auto_refresh_kwargs=_,
             token_updater=_)
 
+    @no_duplicates
+    @patch('tda.auth.Client')
+    @patch('tda.auth.OAuth2Session')
+    def test_api_key_is_normalized(self, session, client):
+        self.write_token()
+
+        client.return_value = 'returned client'
+
+        self.assertEqual('returned client',
+                         auth.client_from_token_file(self.pickle_path, 'API_KEY'))
+        client.assert_called_once_with('API_KEY@AMER.OAUTHAP', _)
+        session.assert_called_once_with(
+            'API_KEY@AMER.OAUTHAP',
+            token=self.token,
+            auto_refresh_url=_,
+            auto_refresh_kwargs=_,
+            token_updater=_)
+
 
 REDIRECT_URL = 'https://redirect.url.com'
 
@@ -82,6 +100,32 @@ class ClientFromLoginFlow(unittest.TestCase):
         with open(self.pickle_path, 'rb') as f:
             self.assertEqual(self.token, pickle.load(f))
 
+    @no_duplicates
+    @patch('tda.auth.Client')
+    @patch('tda.auth.OAuth2Session')
+    def test_normalize_api_key(self, session_constructor, client):
+        AUTH_URL = 'https://auth.url.com'
+
+        session = MagicMock()
+        session_constructor.return_value = session
+        session.authorization_url.return_value = AUTH_URL, None
+        session.fetch_token.return_value = self.token
+
+        webdriver = MagicMock()
+        webdriver.get.return_value = REDIRECT_URL + '/token_params'
+
+        client.return_value = 'returned client'
+
+        self.assertEqual('returned client',
+                         auth.client_from_login_flow(
+                             webdriver, 'API_KEY', REDIRECT_URL,
+                             self.pickle_path,
+                             redirect_wait_time_seconds=0.0))
+
+        self.assertEqual(
+                'API_KEY@AMER.OAUTHAP',
+                session_constructor.call_args[0][0])
+
 
 class EasyClientTest(unittest.TestCase):