Skip to main content

Overview

Fantasy Basketball Analytics uses Yahoo OAuth 2.0 to securely access your fantasy league data. The authentication system handles login, token management, and automatic token refresh to maintain your session.

OAuth Flow

The authentication process follows the standard OAuth 2.0 authorization code flow:

1. Initial Login

When you click “Login with Yahoo” on the home page, the application initiates the OAuth flow:
main.py
@app.route("/login")
def login():
    return yahoo.authorize_redirect(
        url_for("callback", _external=True, _scheme="https")
    )
This redirects you to Yahoo’s authorization page where you grant permission for the app to access your fantasy basketball data.
The app requests the fspt-r scope, which grants read-only access to your fantasy sports data. It cannot make changes to your teams or leagues.

2. Authorization Callback

After you approve access, Yahoo redirects back to the callback endpoint:
main.py
@app.route("/callback")
def callback():
    session["token"] = yahoo.authorize_access_token()
    return redirect(url_for("select"))
The callback:
  • Exchanges the authorization code for an access token
  • Stores the token in your Flask session
  • Redirects you to the league selection page

3. Session Storage

Your authentication token is stored in a server-side session with these security settings:
main.py
app.config.update(
    SESSION_COOKIE_SAMESITE="Lax",
    SESSION_COOKIE_SECURE=False,
    PERMANENT_SESSION_LIFETIME=60 * 60,  # 1 hour
)
  • SESSION_COOKIE_SAMESITE=“Lax”: Provides CSRF protection while allowing normal navigation
  • PERMANENT_SESSION_LIFETIME=60 * 60: Session expires after 1 hour of inactivity
  • The session uses Flask’s secret key for encryption

Token Refresh Mechanism

Yahoo access tokens expire after a short period. The app automatically refreshes them when needed:
main.py
def _refresh_token() -> Dict[str, Any]:
    old = session.get("token", {})
    r = yahoo.refresh_token(
        yahoo.refresh_token_url, 
        refresh_token=old.get("refresh_token")
    )
    session["token"] = r
    log.info("🔑  Yahoo token refreshed at %s", time.strftime("%H:%M:%S"))
    return r

Automatic Retry Logic

When making API calls, the system automatically detects expired tokens and refreshes them:
main.py
def yahoo_api(rel_path: str, *, _retry: bool = True) -> Dict[str, Any]:
    token = session["token"]
    resp = requests.get(
        f"https://fantasysports.yahooapis.com/{rel_path}",
        headers={"Authorization": f"Bearer {token['access_token']}"},
        params={"format": "json"},
        timeout=20,
    )
    if resp.status_code == 401 and _retry:
        _refresh_token()  # Refresh the token
        return yahoo_api(rel_path, _retry=False)  # Retry once
    resp.raise_for_status()
    return resp.json()
The _retry parameter prevents infinite loops by ensuring the request is only retried once after token refresh.

Session Management

Protected Routes

Most routes check for authentication before proceeding:
main.py
@app.route("/select")
def select():
    if "token" not in session:
        return redirect(url_for("index"))
    # ... fetch leagues

@app.route("/dashboard")
def dashboard():
    if "league_key" not in session:
        return redirect(url_for("select"))
    # ... render dashboard

Logging Out

The logout route clears your session data:
main.py
@app.route("/logout")
def logout():
    session.clear()
    return redirect(url_for("index"))
  1. All session data is cleared (tokens, league selection, team name)
  2. You’re redirected to the home page
  3. Your browser’s session cookie is invalidated
  4. You’ll need to re-authenticate with Yahoo to access the app again

Security Considerations

Environment Variables

Yahoo OAuth credentials are stored as environment variables:
main.py
yahoo = oauth.register(
    name="yahoo",
    client_id=os.environ["YAHOO_CLIENT_ID"],
    client_secret=os.environ["YAHOO_CLIENT_SECRET"],
    authorize_url="https://api.login.yahoo.com/oauth2/request_auth",
    access_token_url="https://api.login.yahoo.com/oauth2/get_token",
    refresh_token_url="https://api.login.yahoo.com/oauth2/get_token",
)
Never commit your YAHOO_CLIENT_ID and YAHOO_CLIENT_SECRET to version control. Use a .env file for local development.

Token Security

  • Access tokens are never exposed to the client browser
  • All API calls are proxied through the Flask backend
  • Tokens are encrypted in the session cookie using Flask’s secret key
  • Sessions expire after 1 hour of inactivity

HTTPS Enforcement

The OAuth callback uses HTTPS in production:
main.py
url_for("callback", _external=True, _scheme="https")
This ensures that authorization codes and tokens are transmitted securely.

Troubleshooting

If you see authentication errors:
  1. The app will automatically try to refresh your token
  2. If refresh fails, you’ll be redirected to log in again
  3. Check that your session hasn’t exceeded the 1-hour timeout
Common issues:
  • Invalid redirect URI: Ensure your Yahoo app settings match the callback URL
  • Missing environment variables: Verify YAHOO_CLIENT_ID and YAHOO_CLIENT_SECRET are set
  • Scope permissions: The app requires the fspt-r scope for fantasy sports data
For local development:
# Create a .env file
YAHOO_CLIENT_ID=your_client_id
YAHOO_CLIENT_SECRET=your_client_secret
FLASK_SECRET_KEY=your_secret_key
The app uses python-dotenv to load these in development mode.

Next Steps

League Selection

Learn how to select and manage your fantasy leagues

Dashboard Navigation

Explore the dashboard interface and features

Build docs developers (and LLMs) love