import requests import typing import json import os import abc from . import configs from . import exceptions from flask import session from requests_oauthlib import OAuth2Session class DiscordOAuth2HttpClient(abc.ABC): """An OAuth2 http abstract base class providing some factory methods. This class is meant to be overridden by :py:class:`flask_discord.DiscordOAuth2Session` and should not be used directly. Attributes ---------- client_id : int The client ID of discord application provided. client_secret : str The client secret of discord application provided. redirect_uri : str The default URL to use to redirect user to after authorization. """ SESSION_KEYS = [ "DISCORD_OAUTH2_STATE", "DISCORD_OAUTH2_TOKEN", ] def __init__(self, app): self.client_id = app.config["DISCORD_CLIENT_ID"] self.client_secret = app.config["DISCORD_CLIENT_SECRET"] self.redirect_uri = app.config["DISCORD_REDIRECT_URI"] if "http://" in self.redirect_uri: os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "true" app.discord = self @staticmethod def _token_updater(token): session["DISCORD_OAUTH2_TOKEN"] = token def _make_session(self, token: str = None, state: str = None, scope: list = None) -> OAuth2Session: """A low level method used for creating OAuth2 session. Parameters ---------- token : str, optional The authorization token to use which was previously received from authorization code grant. state : str, optional The state to use for OAuth2 session. scope : list, optional List of valid `Discord OAuth2 Scopes `_. Returns ------- OAuth2Session An instance of OAuth2Session class. """ return OAuth2Session( client_id=self.client_id, token=token or session.get("DISCORD_OAUTH2_TOKEN"), state=state, scope=scope, redirect_uri=self.redirect_uri, auto_refresh_kwargs={ 'client_id': self.client_id, 'client_secret': self.client_secret, }, auto_refresh_url=configs.DISCORD_TOKEN_URL, token_updater=self._token_updater) def request(self, route: str, method="GET", data=None, oauth=True, **kwargs) -> typing.Union[dict, str]: """Sends HTTP request to provided route or discord endpoint. Note ---- It automatically prefixes the API Base URL so you will just have to pass routes or URL endpoints. Parameters ---------- route : str Route or endpoint URL to send HTTP request to. Example: ``/users/@me`` method : str, optional Specify the HTTP method to use to perform this request. data : dict, optional The optional payload the include with the request. oauth : bool A boolean determining if this should be Discord OAuth2 session request or any standard request. Returns ------- dict, str Dictionary containing received from sent HTTP GET request if content-type is ``application/json`` otherwise returns raw text content of the response. Raises ------ flask_discord.Unauthorized Raises :py:class:`flask_discord.Unauthorized` if current user is not authorized. """ route = configs.DISCORD_API_BASE_URL + route response = self._make_session( ).request(method, route, data, **kwargs) if oauth else requests.request(method, route, data=data, **kwargs) if response.status_code == 401: raise exceptions.Unauthorized try: return response.json() except json.JSONDecodeError: return response.text