Files
Flask-Discord/flask_discord/client.py

145 lines
5.2 KiB
Python

from . import configs, _http, models
from flask import request, session, redirect
from oauthlib.common import add_params_to_uri
class DiscordOAuth2Session(_http.DiscordOAuth2HttpClient):
"""Main client class representing hypothetical OAuth2 session with discord.
It uses Flask `session <http://flask.pocoo.org/docs/1.0/api/#flask.session>`_ local proxy object
to save state, authorization token and keeps record of users sessions across different requests.
This class inherits :py:class:`flask_discord._http.DiscordOAuth2HttpClient` class.
Parameters
----------
app : Flask
An instance of your `flask application <http://flask.pocoo.org/docs/1.0/api/#flask.Flask>`_.
"""
def create_session(self, scope: list = None, prompt: bool = True, params: dict = None):
"""Primary method used to create OAuth2 session and redirect users for
authorization code grant.
Parameters
----------
scope : list, optional
An optional list of valid `Discord OAuth2 Scopes
<https://discordapp.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes>`_.
prompt : bool, optional
Determines if the OAuth2 grant should be explicitly prompted and re-approved. Defaults to True.
Specify False for implicit grant which will skip the authorization screen and redirect to redirect URI.
params : dict, optional
An optional mapping of query parameters to supply to the authorization URL.
Returns
-------
redirect
Flask redirect to discord authorization servers to complete authorization code grant process.
"""
scope = scope or request.args.get("scope", str()).split() or configs.DISCORD_OAUTH_DEFAULT_SCOPES
if not prompt and set(scope) & set(configs.DISCORD_PASSTHROUGH_SCOPES):
raise ValueError("You should use explicit OAuth grant for passthrough scopes like bot.")
discord_session = self._make_session(scope=scope)
authorization_url, state = discord_session.authorization_url(configs.DISCORD_AUTHORIZATION_BASE_URL)
session["DISCORD_OAUTH2_STATE"] = state
prompt = "consent" if prompt else "none"
params = params or dict()
params.update(prompt=prompt)
authorization_url = add_params_to_uri(authorization_url, params)
return redirect(authorization_url)
def callback(self):
"""A method which should be always called after completing authorization code grant process
usually in callback view.
It fetches the authorization token and saves it flask
`session <http://flask.pocoo.org/docs/1.0/api/#flask.session>`_ object.
"""
if request.values.get("error"):
return request.values["error"]
discord = self._make_session(state=session.get("DISCORD_OAUTH2_STATE"))
token = discord.fetch_token(
configs.DISCORD_TOKEN_URL,
client_secret=self.client_secret,
authorization_response=request.url
)
self._token_updater(token)
def revoke(self):
"""This method clears current discord token, state and all session data from flask
`session <http://flask.pocoo.org/docs/1.0/api/#flask.session>`_. Which means user will have
to go through discord authorization token grant flow again. Also tries to remove the user from internal
cache if they exist.
"""
self.users_cache.pop(self.user_id, None)
for session_key in self.SESSION_KEYS:
try:
session.pop(session_key)
except KeyError:
pass
@property
def authorized(self):
"""A boolean indicating whether current session has authorization token or not."""
return self._make_session().authorized
@staticmethod
def fetch_user() -> models.User:
"""This method returns user object from the internal cache if it exists otherwise makes an API call to do so.
Returns
-------
flask_discord.models.User
"""
return models.User.get_from_cache() or models.User.fetch_from_api()
@staticmethod
def fetch_connections() -> list:
"""This method returns list of user connection objects from internal cache if it exists otherwise
makes an API call to do so.
Returns
-------
list
List of :py:class:`flask_discord.models.UserConnection` objects.
"""
user = models.User.get_from_cache()
try:
if user.connections is not None:
return user.connections
except AttributeError:
pass
return models.UserConnection.fetch_from_api()
@staticmethod
def fetch_guilds() -> list:
"""This method returns list of guild objects from internal cache if it exists otherwise makes an API
call to do so.
Returns
-------
list
List of :py:class:`flask_discord.models.Guild` objects.
"""
user = models.User.get_from_cache()
try:
if user.guilds is not None:
return user.guilds
except AttributeError:
pass
return models.Guild.fetch_from_api()