uberfastman / yfpy

Python API wrapper for the Yahoo Fantasy Sports public API (supports NFL, NHL, MLB, and NBA).
https://pypi.org/project/yfpy/
GNU General Public License v3.0
178 stars 46 forks source link

AttributeError: 'OAuth2' object has no attribute 'consumer_key' #45

Closed jeisey closed 1 year ago

jeisey commented 1 year ago

Hello, thank you for putting together this package. I followed the install & setup steps, generated token.json and saved it along with private.json in my /auth folder. I'm having authentication issues with using the class YahooFantasySportsQuery, e.g.,

from pathlib import Path
from yfpy.query import YahooFantasySportsQuery
query = YahooFantasySportsQuery(Path("path/to/backend/auth"), league_id="xxxxx")
query.get_all_yahoo_fantasy_game_keys()

I historically have trouble with Oauth (apologies if this is just something simple I'm missing) and am unsure how to further troubleshoot:

Error log below: (league id / username replaced with placeholders)

---------------------------------------------------------------------------
AttributeError: 'OAuth2' object has no attribute 'consumer_key'
AttributeError                            Traceback (most recent call last)
c:\Users\username\Documents\GitHub\project-name\backend\tests.ipynb Cell 3 line 3
      1 from pathlib import Path
      2 from yfpy.query import YahooFantasySportsQuery
----> 3 query = YahooFantasySportsQuery(Path("C:/Users/username/Documents/GitHub/project-name/backend/auth"), league_id="xxxxxx")
      4 # query.get_all_yahoo_fantasy_game_keys()

File c:\Users\username\Documents\GitHub\project-name\backend\venv\Lib\site-packages\yfpy\query.py:124, in YahooFantasySportsQuery.__init__(self, auth_dir, league_id, game_id, game_code, offline, all_output_as_json_str, consumer_key, consumer_secret, browser_callback, retries, backoff)
    121 self.executed_queries: List[Dict[str, Any]] = []
    123 if not self.offline:
--> 124     self._authenticate()

File c:\Users\username\Documents\GitHub\project-name\backend\venv\Lib\site-packages\yfpy\query.py:165, in YahooFantasySportsQuery._authenticate(self)
    161     self._yahoo_access_token = auth_info["access_token"]
    163 # complete OAuth2 3-legged handshake by either refreshing existing token or requesting account access
    164 # and returning a verification code to input to the command line prompt
--> 165 self.oauth = OAuth2(None, None, from_file=token_file_path, browser_callback=self._browser_callback)
    166 if not self.oauth.token_is_valid():
    167     self.oauth.refresh_access_token()

File c:\Users\username\Documents\GitHub\project-name\backend\venv\Lib\site-packages\yahoo_oauth\oauth.py:236, in OAuth2.__init__(self, consumer_key, consumer_secret, **kwargs)
    235 def __init__(self, consumer_key, consumer_secret, **kwargs):
--> 236     super(OAuth2, self).__init__('oauth2', consumer_key, consumer_secret, **kwargs)
...
     80     'base_url': vars(self).get('base_url', None)
     81 })
     83 # Defining oauth service
AttributeError: 'OAuth2' object has no attribute 'consumer_key'
uberfastman commented 1 year ago

Hi @jeisey I'll do my best to help! Just a few questions so I can better understand what you did... you had no issues generating the token.json file on initial use of YFPY? As in, the browser opened, you approved use, got the code, and input it right?

Could you share the redacted contents of your token.json file? This error is coming from the third party library I use to handle OAuth with Yahoo... can you run pip freeze in your virtualenv and let me know which version of the yahoo_oauth library is installed?

jeisey commented 1 year ago

Thanks @uberfastman appreciate your help.

you had no issues generating the token.json file on initial use of YFPY? As in, the browser opened, you approved use, got the code, and input it right?

Yep, passed the credentials from private.json to https://api.login.yahoo.com/oauth2/request_auth? and then saved the token.js from https://api.login.yahoo.com/oauth2/get_token into the auth folder.

Could you share the redacted contents of your token.json file?

Sure thing, please see below:

token.json
{
    "access_token": "REDACTED",
    "refresh_token": "REDACTED",
    "expires_in": 3600,
    "token_type": "bearer",
    "id_token": "REDACTED"
}

private.json
{
  "consumer_key": "REDACTED",
  "consumer_secret": "REDACTED"
}

And here are the package versions:

yahoo-oauth==2.0
yfpy==13.0.0

My initial thought was I'm just not passing the directory location correctly because it seemed unable to access consumer_key, however I tested "Path" as well as explicitly unpacking the json and passing into the YahooFantasySportsQuery class but receive same error above. Thanks again for help and time, it is much appreciated.

uberfastman commented 1 year ago

Did you pass those credentials to that URL directly using something like Postman or curl @jeisey? I ask because if so, you don't need to do that. If you just run YFPY with the private.json in the auth directory, after approving the app access in the browser and inputting the verifier code back into the console, the process will simply complete on it's own. It will create token.json for you and save it in the auth directory, nothing you have to do for that.

When it does so, it happens to also save the consumer_key and consumer_secret that you initially stored in private.json into the generated token.json, which is what's breaking your current setup because your token.json is missing those two fields, hence your error message.

If you have any questions about the above process let me know, otherwise I'll close this as I believe that should get you sorted!

jeisey commented 1 year ago

I think this is where I'm getting tripped up and really do appreciate your patience @uberfastman - the authentication order of operations is where I'm a little lost still.

  1. Originally I generated the token.json via using https://developer.yahoo.com/oauth2/guide/openid_connect/getting_started.html - is there any utility for the token.json this generated? It sounds like I can discard that one based on your response. For reference this was how I went about the initial authentication:
import webbrowser
import json

# Load credentials from private.json
with open("private.json", "r") as file:
    credentials = json.load(file)

# Construct the URL
auth_url = (
    "https://api.login.yahoo.com/oauth2/request_auth?"
    "client_id={}&"
    "response_type=code&"
    "redirect_uri={}&"
    "scope=openid"
).format(credentials["consumer_key"], "https://localhost:8080")

# Open the URL in a browser
webbrowser.open(auth_url)

import requests

# Fetch from the browser's address bar
authorization_code = "PLACEHOLDER"

# Construct the POST data
data = {
    "client_id": credentials["consumer_key"],
    "client_secret": credentials["consumer_secret"],
    "redirect_uri": "https://localhost:8080",
    "code": authorization_code,
    "grant_type": "authorization_code"
}

# Make the POST request
response = requests.post("https://api.login.yahoo.com/oauth2/get_token", data=data)

# Parse the JSON response
tokens = response.json()

# Save the tokens for future use
with open("token.json", "w") as file:
    json.dump(tokens, file)
  1. I am testing initial queries in a .ipynb in VS Code via commands like:
    
    query = YahooFantasySportsQuery(Path("C:/Users/username/Documents/GitHub/project/backend/auth"), league_id="xxxxx")
    query.get_all_yahoo_fantasy_game_keys()

--Or a variation:

sc = YahooFantasySportsQuery(auth_dir=auth_directory, league_id=league_id, game_code="nfl") league_data = sc.get_league_data(league_id)

Calculate league stats...


3. When I removed the token.json and reran it directed me to a yahoo URL to fetch a code, but I did not see a place for me to enter this code back in the .ipynb file. Is there a different way to go about this to authenticate, specifically where/how I can enter that code? I did look through your documentation for the class YahooFantasySportsQuery here https://yfpy.uberfastman.com/_autosummary/yfpy.query.YahooFantasySportsQuery.html#yfpy.query.YahooFantasySportsQuery.get_league_info but wasn't sure how to authenticate manually. 

4. As for the utility of this project I was hoping to create an interface on the frontend (react.js) that allows anyone to "connect" their yahoo league and the backend (via yfpy) can fetch their league's data. It seemed like once the token.json is sorted out then it can utilize the refresh token to auto-refresh, so there is no manual step needed of fetching a token and manually entering it?
uberfastman commented 1 year ago

No worries @jeisey, happy to clarify. So maybe this is an opportunity for me to make the YFPY documentation more precise, because after creating the Yahoo app you don't need to do do any of the other OAuth stuff yourself, all of that is handled already via YFPY's use of the yahoo_oauth library.

When you run it without an existing token.json, the initial prompt is going to ask for user input, which is where you put the verifier code. From a quick Goggle I see that there are sometimes some issues with Python's input() and Jupyter notebooks, but I tried it out (running in PyCharm for me) and it seems to work just fine, you can enter a response and hit enter and it submits it.

All that being said... I would think based on your above process that if you simply add the consumer_key and consumer_secret values from private.json into the token.json you manually fetched then it ought to still work too, so you might try that?

jeisey commented 1 year ago

Thank you @uberfastman - this is really just me not being great with Oauth process and misinterpreting this line on the readme:

The first time you use YFPY, a browser window will open up asking you to allow your app to access your Yahoo fantasy sports data. You MUST hit allow, and then copy the verification code that pops up into the command line prompt where it will now be asking for verification, hit enter, and the OAuth2 three-legged handshake should be complete and your data should have been successfully retrieved.

If I had simply run the query as you mentioned earlie, a .py would've worked the first time. I was able to input the code by moving the initial test into a .py, running in powershell, and then the .ipynb queries worked. Apologies for my misunderstanding and thank you again for your time and help here.

uberfastman commented 1 year ago

No trouble at all @jeisey, I'm glad you got it working! And I just see these moments as opportunities to improve as well, so I'll be taking a read-through of my README instructions and trying to improve/flesh out any areas that might not be clear or complete, so thanks for the valuable feedback!

Good luck with your project, once you get it up and running I'd love to check it out as it would be cool to see something using YFPY under the hood!