schwab-py
schwab-py copied to clipboard
Access token not refreshing
Description of Bug I created my token with client_from_manual_flow, all my inputs are correct. It then only gives me a expires_in token of 1800 (30 min). Then I use schwab.auth.client_from_token_file to create my client which is also suppose to refresh my token as well right? But my token still expires on the first set 30 min My token file exists on the same level as the code I am using to grab quotes. Here is my code: `from schwab.auth import client_from_manual_flow api_key='', app_secret='', callback_url='https://127.0.0.1/', token_path='/mnt/c/Users/Brogan/Documents/tickerStats/schwab-py/token.json' def getPrice(ticker): client = schwab.auth.client_from_token_file(token_path, api_key, app_secret)
r = client.get_quote(ticker)
r=r.json() print(r) getPrice('$SPX')` Expected Behavior I expect the access token to update to a new expire time. Actual Behavior Instead, the token still expires at my original set time from the manual token creation. I have to manually create the token every 30 min to access schwab Error/Exception Log, If Applicable
Passing client fetch log:
schwab-api version 0.0.0a29 Loading token from file /mnt/c/Users/Brogan/Documents/tickerStats/schwab-py/token.json Req 1: GET to https://api.schwabapi.com/marketdata/v1/$SPX/quotes, params={} Req 1: GET response: 200, content={"$SPX":{"assetMainType":"INDEX","realtime":true,"ssid":1819771877,"symbol":"$SPX","fundamental":{"avg10DaysVolume":0.0,"avg1YearVolume":0.0,"divAmount":0.0,"divFreq":0,"divPayAmount":0.0,"divYield":0.0,"eps":0.0,"fundLeverageFactor":0.0,"peRatio":0.0},"quote":{"52WeekHigh":5325.49,"52WeekLow":4103.78,"closePrice":5308.13,"highPrice":5324.32,"lastPrice":5321.41,"lowPrice":5297.87,"netChange":13.28,"netPercentChange":0.25018227,"openPrice":5298.69,"securityStatus":"Closed","totalVolume":1974738504,"tradeTime":1716325754901},"reference":{"description":"S\u0026P 500 INDEX","exchange":"0","exchangeName":"Index"}}} {'$SPX': {'assetMainType': 'INDEX', 'realtime': True, 'ssid': 1819771877, 'symbol': '$SPX', 'fundamental': {'avg10DaysVolume': 0.0, 'avg1YearVolume': 0.0, 'divAmount': 0.0, 'divFreq': 0, 'divPayAmount': 0.0, 'divYield': 0.0, 'eps': 0.0, 'fundLeverageFactor': 0.0, 'peRatio': 0.0}, 'quote': {'52WeekHigh': 5325.49, '52WeekLow': 4103.78, 'closePrice': 5308.13, 'highPrice': 5324.32, 'lastPrice': 5321.41, 'lowPrice': 5297.87, 'netChange': 13.28, 'netPercentChange': 0.25018227, 'openPrice': 5298.69, 'securityStatus': 'Closed', 'totalVolume': 1974738504, 'tradeTime': 1716325754901}, 'reference': {'description': 'S&P 500 INDEX', 'exchange': '0', 'exchangeName': 'Index'}}} BEGIN REDACTED LOGS [debug.py:151:_enable_bug_report_logging] schwab-api version 0.0.0a29 [auth.py:36:load_token] Loading token from file /mnt/c/Users/Brogan/Documents/tickerStats/schwab-py/token.json [synchronous.py:17:_get_request] Req 1: GET to https://api.schwabapi.com/marketdata/v1/$SPX/quotes, params={} [base.py:64:_log_response] Req 1: GET response: 200, content={"$SPX":{"assetMainType":"INDEX","realtime":true,"ssid":1819771877,"symbol":"$SPX","fundamental":{"avg10DaysVolume":0.0,"avg1YearVolume":0.0,"divAmount":0.0,"divFreq":0,"divPayAmount":0.0,"divYield":0.0,"eps":0.0,"fundLeverageFactor":0.0,"peRatio":0.0},"quote":{"52WeekHigh":5325.49,"52WeekLow":4103.78,"closePrice":5308.13,"highPrice":5324.32,"lastPrice":5321.41,"lowPrice":5297.87,"netChange":13.28,"netPercentChange":0.25018227,"openPrice":5298.69,"securityStatus":"Closed","totalVolume":1974738504,"tradeTime":1716325754901},"reference":{"description":"S\u0026P 500 INDEX","exchange":"0","exchangeName":"Index"}}}
Failing client fetch log:
schwab-api version 0.0.0a29 Loading token from file token.json Req 1: GET to https://api.schwabapi.com/marketdata/v1/$SPX/quotes, params={} Traceback (most recent call last): File "get_quote.py", line 27, in getPrice('$SPX') File "get_quote.py", line 23, in getPrice r = client.get_quote(ticker) File "/mnt/c/Users/Brogan/Documents/tickerStats/schwab-py/schwab/client/base.py", line 456, in get_quote return self._get_request(path, params) File "/mnt/c/Users/Brogan/Documents/tickerStats/schwab-py/schwab/client/synchronous.py", line 19, in _get_request resp = self.session.get(dest, params=params) File "/home/brmcgraw/.local/lib/python3.6/site-packages/httpx/_client.py", line 983, in get timeout=timeout, File "/home/brmcgraw/.local/lib/python3.6/site-packages/authlib/integrations/httpx_client/oauth2_client.py", line 196, in request self.ensure_active_token() File "/home/brmcgraw/.local/lib/python3.6/site-packages/authlib/integrations/httpx_client/oauth2_client.py", line 207, in ensure_active_token self.refresh_token(url, refresh_token=refresh_token) File "/home/brmcgraw/.local/lib/python3.6/site-packages/authlib/oauth2/client.py", line 264, in refresh_token auth=auth, **session_kwargs) File "/home/brmcgraw/.local/lib/python3.6/site-packages/authlib/oauth2/client.py", line 275, in _refresh_token token = self.parse_response_token(resp.json()) File "/home/brmcgraw/.local/lib/python3.6/site-packages/authlib/oauth2/client.py", line 380, in parse_response_token self.handle_error(error, description) File "/home/brmcgraw/.local/lib/python3.6/site-packages/authlib/integrations/httpx_client/oauth2_client.py", line 188, in handle_error raise OAuthError(error_type, error_description) authlib.integrations.base_client.errors.OAuthError: invalid_client: Unauthorized BEGIN REDACTED LOGS [debug.py:151:_enable_bug_report_logging] schwab-api version 0.0.0a29 [auth.py:36:load_token] Loading token from file token.json [synchronous.py:17:_get_request] Req 1: GET to https://api.schwabapi.com/marketdata/v1/$SPX/quotes, params={}
I want to note that my callback url does not have the / at the end. When posting in here it automatically adds it. My callback in my code and app are an exact copy of eachother
I did just notice when I do the client_from_manual_flow, my callback url has the backslash, not sure if this is right: https://127.0.0.1/?code=C0.........
I want to note that my callback url does not have the / at the end. When posting in here it automatically adds it. My callback in my code and app are an exact copy of eachother
This looks like a quirk of the way github displays URLs. Next time, please wrap your code in triple backticks to format it properly.
Adding some more context. I re-ran everything with python3.11 including the manual token creator. I get this error after 30 min:
Req 1: GET to https://api.schwabapi.com/marketdata/v1/SPY/quotes, params={}
Traceback (most recent call last):
File "/mnt/c/Users/Brogan/Documents/tickerStats/temp/get_quote.py", line 32, in <module>
getPrice('SPY')
File "/mnt/c/Users/Brogan/Documents/tickerStats/temp/get_quote.py", line 28, in getPrice
r = client.get_quote(ticker)
^^^^^^^^^^^^^^^^^^^^^^^^
File "/mnt/c/Users/Brogan/Documents/tickerStats/Temp/schwab-py/schwab/client/base.py", line 469, in get_quote
return self._get_request(path, params)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/mnt/c/Users/Brogan/Documents/tickerStats/Temp/schwab-py/schwab/client/synchronous.py", line 19, in _get_request
resp = self.session.get(dest, params=params)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/brmcgraw/.local/lib/python3.11/site-packages/httpx/_client.py", line 1054, in get
return self.request(
^^^^^^^^^^^^^
File "/home/brmcgraw/.local/lib/python3.11/site-packages/authlib/integrations/httpx_client/oauth2_client.py", line 201, in request
if not self.ensure_active_token(self.token):
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/brmcgraw/.local/lib/python3.11/site-packages/authlib/oauth2/client.py", line 266, in ensure_active_token
self.refresh_token(url, refresh_token=refresh_token)
File "/home/brmcgraw/.local/lib/python3.11/site-packages/authlib/oauth2/client.py", line 256, in refresh_token
return self._refresh_token(
^^^^^^^^^^^^^^^^^^^^
File "/home/brmcgraw/.local/lib/python3.11/site-packages/authlib/oauth2/client.py", line 372, in _refresh_token
resp = self._http_post(url, body=body, auth=auth, headers=headers, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/brmcgraw/.local/lib/python3.11/site-packages/authlib/oauth2/client.py", line 426, in _http_post
return self.session.post(
^^^^^^^^^^^^^^^^^^
File "/home/brmcgraw/.local/lib/python3.11/site-packages/httpx/_client.py", line 1145, in post
return self.request(
^^^^^^^^^^^^^
File "/home/brmcgraw/.local/lib/python3.11/site-packages/authlib/integrations/httpx_client/oauth2_client.py", line 206, in request
return super().request(
^^^^^^^^^^^^^^^^
File "/home/brmcgraw/.local/lib/python3.11/site-packages/httpx/_client.py", line 827, in request
return self.send(request, auth=auth, follow_redirects=follow_redirects)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/brmcgraw/.local/lib/python3.11/site-packages/httpx/_client.py", line 914, in send
response = self._send_handling_auth(
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/brmcgraw/.local/lib/python3.11/site-packages/httpx/_client.py", line 939, in _send_handling_auth
request = next(auth_flow)
^^^^^^^^^^^^^^^
File "/home/brmcgraw/.local/lib/python3.11/site-packages/httpx/_auth.py", line 72, in sync_auth_flow
request = next(flow)
^^^^^^^^^^
File "/home/brmcgraw/.local/lib/python3.11/site-packages/authlib/integrations/httpx_client/oauth2_client.py", line 43, in auth_flow
url, headers, body = self.prepare(
^^^^^^^^^^^^^
File "/home/brmcgraw/.local/lib/python3.11/site-packages/authlib/oauth2/auth.py", line 67, in prepare
return self.auth_method(self, method, uri, headers, body)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/brmcgraw/.local/lib/python3.11/site-packages/authlib/oauth2/auth.py", line 10, in encode_client_secret_basic
text = f'{quote(client.client_id)}:{quote(client.client_secret)}'
^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/urllib/parse.py", line 868, in quote
return quote_from_bytes(string, safe)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/urllib/parse.py", line 898, in quote_from_bytes
raise TypeError("quote_from_bytes() expected bytes")
TypeError: quote_from_bytes() expected bytes
### BEGIN REDACTED LOGS ###
[debug.py:151:_enable_bug_report_logging] schwab-api version 1.0.0
[synchronous.py:16:_get_request] Req 1: GET to https://api.schwabapi.com/marketdata/v1/SPY/quotes, params={}```
Are you consistently seeing this error?
I haven't received any new instances of this issue since this change was released, so I'm marking this issue as closed. If anyone receives a 401 error, please reopen this issue and add your experience.